#!/usr/bin/env python

import mango
import mango.io
import mango.image
import mango.fmm
import mango.mpi as mpi

import scipy as sp
import numpy as np
import logging
import re
import os
import os.path
import sys

haveArgParse = False
try:
    import argparse
    haveArgParse = True
except:
    import optparse

logger, rootLogger = mpi.getLoggers(__name__)

def elemfreq(mskDds):
    return mango.itemfreq(mskDds)

_sphericalCavityLabelerCount = 0
class SphericalCavityLabeler:
    """
    Performs convex hull mask of skull, calculates max-covering-radius
    transform on the cavity voxels and subsequent labelling of the cavities.
    """

    def __init__(self, outDir=None, outSuffix=None, outExt=None):
        global _sphericalCavityLabelerCount
        if ((outDir == None) or (outDir == "")):
            outDir = "."
        if (outSuffix == None):
            outSuffix = "%02d" % _sphericalCavityLabelerCount
        if (outExt == None):
            outExt = ".nc"

        self.outDir     = outDir
        self.outSuffix  = outSuffix
        self.outExt     = outExt

        self.se = mango.image.sphere_se(3)
        self.writeIntermediateImages = False

        _sphericalCavityLabelerCount += 1

    def writeIntermediateDds(self, intermediateSuffix, dds):
        if (self.writeIntermediateImages):
            outPath = \
                os.path.join(
                    self.outDir,
                    (
                        "%s%s%s%s"
                        %
                        (
                            dds.mtype.fileNameBase(),
                            self.outSuffix,
                            intermediateSuffix,
                            self.outExt
                        )
                     )
                )
            mango.io.writeDds(outPath, dds)

    def maskLowStddVoxels(self, dds, nMeanDds, nStddDds):
        unique = np.unique(sp.where(nStddDds.subd.asarray() <= 1.0/3, dds.subd.asarray(), dds.mtype.maskValue()))
        unique = unique[sp.where(unique != dds.mtype.maskValue())]
        if (dds.mpi.comm != None):
            unique = dds.mpi.comm.allreduce(unique.tolist(), op=mpi.SUM)
            unique = np.unique(unique)
        rootLogger.info("Unique constant stdd values = %s" % (unique,))
        rootLogger.info("Creating mask from unique constant values...")
        mskDds = mango.zeros_like(dds, mtype="segmented")
        for uVal in unique:
            mskDds.asarray()[...] = sp.where(dds.asarray() == uVal, 1, mskDds.asarray())
        rootLogger.info("Done creating mask from unique constant values.")

        rootLogger.info("Labeling connected constant zero-stdd regions...")
        mskDds.updateHaloRegions()
        mskDds.mirrorOuterLayersToBorder(False)
        self.writeIntermediateDds("_000ZeroStddForLabeling", mskDds)
        lblDds = mango.image.label(mskDds, 1)
        rootLogger.info("Done labeling connected constant stdd regions.")
        self.writeIntermediateDds("_000ZeroStdd", lblDds)

        countThresh = 0.01 * sp.product(lblDds.shape)
        rootLogger.info("Eliminating large clusters...")
        lblDds = mango.image.eliminate_labels_by_size(lblDds, minsz=int(countThresh), val=lblDds.mtype.maskValue())
        self.writeIntermediateDds("_000ZeroStddLargeEliminated", lblDds)

        rootLogger.info("Assigning mask values...")
        mskDds.subd.asarray()[...] = \
            sp.where(lblDds.subd.asarray() == lblDds.mtype.maskValue(), True, False)
        self.writeIntermediateDds("_000ZeroStddMskd", mskDds)
        del lblDds
        for tmpDds in [dds, nMeanDds, nStddDds]:
            tmpDds.subd.asarray()[...] = \
                sp.where(mskDds.subd.asarray(), tmpDds.mtype.maskValue(), tmpDds.subd.asarray())

    def eliminateSmallClusters(self, mskDds, clusterSzThreshFraction):
        """
        Performs a labelling of non-mask-value connected components of
        the :samp:`mskDds` image and eliminates clusters/objects
        which have number of voxels which is less
        than :samp:`clusterSzThreshFraction*mango.count_non_masked(mskDds)`.
        :type mskDds: :obj:`mango.Dds`
        :param mskDds: This image is modified by eliminating small clusters/objects
           (by setting small-cluster-voxels to value :samp:`mskDds.mtype.maskValue()`.
        :type clusterSzThreshFraction: :obj:`float`
        :param clusterSzThreshFraction: Value in interval :samp:`[0,1]`. Threshold fraction of
           total non-masked :samp:`mskDds` voxels for eliminating small clusters/objects.
        """
        elimClustersSmallerThan = int(sp.ceil(mango.count_non_masked(mskDds)*clusterSzThreshFraction))
        segDds = mango.ones_like(mskDds, mtype="segmented")
        mango.copy_masked(mskDds, segDds)
        rootLogger.info("eliminateSmallClusters: Labeling mskDds masked connected components...")
        lblDds = mango.image.label(segDds, 1)
        rootLogger.info("eliminateSmallClusters: Done labeling mskDds masked connected components...")
        self.writeIntermediateDds("_111MskDdsLabels", lblDds)
        rootLogger.info("eliminateSmallClusters: Eliminating clusters of size range [%s, %s]..." % (0, elimClustersSmallerThan))
        lblDds = mango.image.eliminate_labels_by_size(lblDds, val=lblDds.mtype.maskValue(), minsz=0, maxsz=elimClustersSmallerThan)
        rootLogger.info("eliminateSmallClusters: Done eliminating clusters in size range [%s, %s]." % (0, elimClustersSmallerThan))
        rootLogger.info("eliminateSmallClusters: Copying small-cluster mask to mskDds...")
        mango.copy_masked(lblDds, mskDds)
        rootLogger.info("eliminateSmallClusters: Done copying small-cluster mask to mskDds.")

    def eliminatePercentileTails(self, mskDds, loPercentile=10.0, hiPercentile=90.0):
        """
        Trims lower and/or upper image histogram tails by replacing :samp:`mskDds`
        voxel values with :samp:`mskDds.mtype.maskValue()`.
        """
        rootLogger.info("Eliminating percentile tails...")
        rootLogger.info("Calculating element frequencies...")
        elems, counts = elemfreq(mskDds)
        rootLogger.info("elems:\n%s" % (elems,))
        rootLogger.info("counts:\n%s" % (counts,))
        cumSumCounts = sp.cumsum(counts, dtype="float64")
        percentiles = 100.0*(cumSumCounts/float(cumSumCounts[-1]))
        percentileElems = elems[sp.where(sp.logical_and(percentiles > loPercentile, percentiles < hiPercentile))]
        loThresh = percentileElems[0]
        hiThresh = percentileElems[-1]
        rootLogger.info("Masking percentiles range (%s,%s) = (%s,%s)" % (loPercentile, hiPercentile, loThresh, hiThresh))
        mskDds.asarray()[...] = \
            sp.where(
                sp.logical_and(
                    sp.logical_and(mskDds.asarray() >= loThresh, mskDds.asarray() <= hiThresh),
                    mskDds.asarray() != mskDds.mtype.maskValue()
                ),
                mskDds.asarray(),
                mskDds.mtype.maskValue()
            )
        rootLogger.info("Done eliminating percentile tails.")

    def calcSphericalCavityLabels(self, dds):

        mpidims = dds.mpi.shape
        if (dds.mpi.comm != None):
            mpidims = (dds.mpi.comm.Get_size(), 1, 1)
        dds = mango.copy(dds, mpidims=mpidims)
        rootLogger.info("Calculating neighbourhood mean image...")
        fltDds = mango.copy(dds, mtype="tomo_float", halo=self.se.getHaloSize())
        nMeanDds = mango.image.mean_filter(fltDds, self.se)
        nMeanDds = mango.copy(nMeanDds, halo=(0,0,0))
        rootLogger.info("Calculating neighbourhood stdd image...")
        nStddDds = mango.image.stdd_filter(fltDds, self.se)
        nStddDds = mango.copy(nStddDds, halo=(0,0,0))
        del fltDds
        self.maskLowStddVoxels(dds, nMeanDds, nStddDds)
        rootLogger.info("Calculating mean vs stdd histogram...")
        h2d, edges = mango.image.histogramdd([nMeanDds, nStddDds], bins=(1024, 8192))
        h2d = h2d[:, :-2]
        rootLogger.info("h2d.shape = %s, edges[0].shape=%s, edges[1].shape=%s" % (h2d.shape, edges[0].shape, edges[1].shape))
        rootLogger.info("Done calculating mean vs stdd histogram...")
        maxIdx = np.unravel_index(np.argmax(h2d), h2d.shape)
        rootLogger.info("np.argmax(h2d) = %s" % (maxIdx,))
        backgroundMean = 0.5 * (edges[0][maxIdx[0]] + edges[0][maxIdx[0] + 1])
        backgroundStdd = 0.5 * (edges[1][maxIdx[1]] + edges[1][maxIdx[1] + 1])

        mskDds = mango.copy(dds)
        rootLogger.info("Background (mean,stdd) = (%s, %s)." % (backgroundMean, backgroundStdd))
        mskDds.subd.asarray()[...] = \
            sp.where(
                sp.logical_or(
                    nStddDds.subd.asarray() < (8 * backgroundStdd),
                    nMeanDds.subd.asarray() < (backgroundMean + 3 * backgroundStdd),
                ),
                mskDds.mtype.maskValue(),
                mskDds.subd.asarray()
            )
        del nMeanDds, nStddDds

        self.writeIntermediateDds("_AaaPrePercentileTailMask", mskDds)
        self.eliminatePercentileTails(mskDds, 1.0, 92.5)
        rootLogger.info("Calculating neighbourhood stdd image...")
        nStddDds = mango.image.stdd_filter(mskDds, self.se)
        self.eliminatePercentileTails(nStddDds, 33.0, 100.0)
        rootLogger.info("Copying stdd percentile tail mask to mskDds...")
        mango.copy_masked(nStddDds, mskDds)
        rootLogger.info("Done copying stdd percentile tail mask to mskDds.")
        self.writeIntermediateDds("_AaaPstPercentileTailMask", mskDds)
        rootLogger.info("Eliminating small clusters from mskDds...")
        self.eliminateSmallClusters(mskDds, 0.1)
        rootLogger.info("Done eliminating small clusters from mskDds.")
        self.writeIntermediateDds("_AaaPstSmallClusterMask", mskDds)
        del nStddDds
        segEdtDds = mango.zeros_like(mskDds, mtype="segmented")
        segEdtDds.asarray()[...] = sp.where(mskDds.asarray() == mskDds.mtype.maskValue(), 0, 1)
        self.writeIntermediateDds("_AaaPreCvxHullMask", segEdtDds)
        rootLogger.info("Calculating convex hull...")
        cvxHullMsk = mango.image.convex_hull_3d(segEdtDds, inputmsk=0, outhull=segEdtDds.mtype.maskValue(), inhull=1)
        segEdtDds.asarray()[...] = sp.where(cvxHullMsk.asarray() == cvxHullMsk.mtype.maskValue(), 1, segEdtDds.asarray())
        rootLogger.info("Done calculating convex hull.")

        self.writeIntermediateDds("_AaaPstCvxHullMask", segEdtDds)
        segEdtDds.setFacesToValue(1)
        rootLogger.info("Calculating EDT image...")
        edtDds = mango.image.distance_transform_edt(segEdtDds, val=0)
        self.writeIntermediateDds("_AaaPstCvxHullMaskEdt", edtDds)
        rootLogger.info("Calculating MCR image...")
        mcrDds = mango.image.max_covering_radius(edtDds, maxdist=0.5*(np.min(edtDds.shape)), filecache=True)
        mango.copy_masked(cvxHullMsk, mcrDds)
        rootLogger.info("Calculating (min,max) MCR values...")
        mcrMin, mcrMax = mango.minmax(mcrDds)
        rootLogger.info("Masking small MCR values...")
        mcrDds.asarray()[...] = sp.where(mcrDds.asarray() >= 0.05*mcrMax, mcrDds.asarray(), mcrDds.mtype.maskValue())
        self.writeIntermediateDds("_AaaPstCvxHullMaskMcr", mcrDds)
        del cvxHullMsk, edtDds
        #
        # Normalise the intensities so that a relative-gradient is computed for the largest
        # MCR radii.
        rootLogger.info("Normalising MCR image...")
        mnmx = mango.minmax(mcrDds)
        tmpDds = mango.copy(mcrDds)
        tmpDds.asarray()[...] -= mnmx[1]
        tmpDds.asarray()[...] *= tmpDds.asarray()
        tmpDds.asarray()[...] = 1+mnmx[1]*np.exp(-(tmpDds.asarray())/(2*0.133*0.133*(mnmx[1]*mnmx[1])))
        mcrDds.asarray()[...] = sp.where(mcrDds.asarray() > 0, mcrDds.asarray()/tmpDds.asarray(), mcrDds.asarray())
        rootLogger.info("Calculating MCR image gradient...")
        grdMcrDds = mango.image.discrete_gaussian_gradient_magnitude(mcrDds, 0.65, errtol=0.01)
        grdMcrDds.asarray()[...] = sp.where(grdMcrDds.asarray() <= 3.0e-2, mcrDds.asarray(), mcrDds.mtype.maskValue())
        rootLogger.info("Calculating unique MCR low-gradient values...")
        u = mango.unique(grdMcrDds)
        rootLogger.info("Converting low gradient MCR image to binary segmentation...")
        segDds = mango.map_element_values(grdMcrDds, lambda x: x in u, mtype="segmented")
        rootLogger.info("Labeling low gradient MCR regions...")
        mango.copy_masked(mcrDds, segDds)
        lblDds = mango.image.label(segDds, val=1, connectivity=26, dosort=True)
        self.writeIntermediateDds("_AaaPstCvxHullMaskMcrGrdLbl", lblDds)
        del segDds, grdMcrDds

        rootLogger.info("Calculating Principal Moment of Inertia...")
        self.pmoi, self.pmoi_axes, self.com = mango.image.moment_of_inertia(mskDds)
        rootLogger.info("Done calculating Principal Moment of Inertia.")
        return lblDds, mcrDds, segEdtDds

def getArgumentParser():
    """
    Returns object for parsing command line options.
    :rtype: argparse.ArgumentParser
    :return: Object to parse command line options.
    """
    descStr = \
        (
            "Labels spherical cavity regions of skull image."
        )

    argList = []

    argList.append(
        {
             'cmdLine':['-l', '--logging-level'],
             'dest':'loggingLevel',
             'type':str,
             'metavar':'LVL',
             'default':"INFO",
             'action':'store',
             'help':"Level of logging output (one of 'CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG')."
        }
    )

    argList.append(
        {
             'cmdLine':['-v', '--mango-verbosity'],
             'dest':'mangoVerbosity',
             'type':str,
             'metavar':'LVL',
             'default':"low",
             'action':'store',
             'help':"Level of mango C++ logging verbosity level (one of 'none', 'low', 'medium', 'high', 'extreme')."
        }
    )

    argList.append(
        {
             'cmdLine':['-s', '--save-intermediates'],
             'dest':'saveIntermediates',
             'type':str,
             'metavar':'LVL',
             'default':"False",
             'action':'store',
             'help':"If True, (a lot of) intermediate images are written to file."
        }
    )

    if (haveArgParse):
        parser = argparse.ArgumentParser(description=descStr)

        parser.add_argument(
            'fileNameList',
            metavar='F',
            type=str,
            nargs='+',
            help='Mango NetCDF image files/dirs.'
        )

        for arg in argList:
            addArgumentDict = dict(arg)
            del addArgumentDict['cmdLine']
            parser.add_argument(*arg['cmdLine'], **addArgumentDict)
    else:
        parser = optparse.OptionParser(description=descStr)
        for arg in argList:
            addOptionDict = dict(arg)
            del addOptionDict['cmdLine']
            parser.add_option(*arg['cmdLine'], **addOptionDict)

    return parser

if (__name__ == "__main__"):
    argParser = getArgumentParser()
    if haveArgParse:
        args = argParser.parse_args()
        argv = args.fileNameList
    else:
        (args, argv) = argParser.parse_args()

    mpi.initialiseLoggers(
        [__name__, "mango.core", "mango.image", "mango.io", "mango.application"],
        logLevel=getattr(logging, args.loggingLevel)
    )
    mango.setLoggingVerbosityLevel(args.mangoVerbosity)
    ddsFileNameList = argv
    args.saveIntermediates = eval(args.saveIntermediates)

    for ddsFileName in ddsFileNameList:
        ucDdsFileName = mango.io.uncompressDdsData(ddsFileName, preserve=False)
        if (not os.path.isdir(ddsFileName)):
            ddsFileName = ucDdsFileName[0]
        imgDds = mango.io.readDds(ddsFileName)
        inDir, inPrefix, inSuffix, inExt = mango.io.splitpath(ddsFileName)
        outDir = inDir
        outSuffix = inSuffix
        outExt = inExt
        scl = SphericalCavityLabeler(outDir, outSuffix, outExt)
        scl.writeIntermediateImages = args.saveIntermediates
        sclDds, mcrDds, segEdtDds = scl.calcSphericalCavityLabels(imgDds)

        for p in ((segEdtDds, "SCLsegedt"), (mcrDds, "SCLmcr"), (sclDds, "SCLlbl")):
            outFileName = os.path.join(outDir, "%s%s%s_%s%s" % (outDir, p[0].mtype.fileNameBase(), outSuffix, p[1], outExt))
            mango.io.writeDds(outFileName, p[0])


import pytest
from hypothesis import given, settings, assume
import hypothesis.strategies as st
import random

import bloopy.utils as utils

@given(st.integers(min_value=1, max_value=1e6), st.integers(min_value=0))
@settings()
def test_create_bitstring_hypothesis(length, val):
    assume(2**length - 1 >= val)
    bstr = utils.create_bitstring_from_int(val, length)
    bstr.reverse()
    transform = int(bstr.to01(),2)
    assert transform == val

def test_create_bitstring(tries=10):
    for t in range(tries):
        length = random.randint(1, 10)
        val = random.randint(0, 2**length -1)
        bstr = utils.create_bitstring_from_int(val, length)
        # One runs from left to right, other right to left.
        bstr.reverse()
        transform = int(bstr.to01(),2)
        assert transform == val, "Failed test fitness function, encoding-decoding bitarray"

if __name__ == '__main__':
    test_create_bitstring()


'http://stackoverflow.com/questions/743806/split-string-into-a-list-in-python'
'http://stackoverflow.com/questions/930397/getting-the-last-element-of-a-list-in-python'

'''
PSEUDO CODE

A <- User Input
B <- Function to split string at spacing stored as a List
C <- Empty List

 RECURSIVEF[x,y]
    IF [x] is empty THEN
        OUTPUT ("No Text Entered")
        RETURN [y]
    ELSE
        [y]appended to read next element in the List from the end
        Last element in [x]List is removed
        return back to the function [x,y]

 CALL RECURSIVEF
OUTPUT [C]

'''

'''
.split divides a string at the spacing and stored in Listing
List is read backwards and displayed for each element read and returned back
to the function
This is done by starting to read from the end of the list to the front
When returned back it is then  outputted for the user to see
'''

import sys

String = input("Enter text here ")
Listing = String.split()
Reversed = []

def RecursiveF(UserList, EmptyList):
    if UserList == []: #1
        return EmptyList #1
        print ("No Text Entered") #1
    else:
        EmptyList.append(UserList[-1]) #1
        UserList.pop(-1) #1
        return RecursiveF(UserList, EmptyList) #1

print(RecursiveF(Listing, Reversed)) #1

sys.exit()

'''
I can see that my program had a notation of +8
+8 is the only and largest magnitude and so is the only significant notation
When it comes to program efficiency
On a graph 8n is equivalent to the (O)n linear graph
'''


import os
from flask import Response
from flask import jsonify
from flask import request
from flask import render_template
from glassserver import app
from glassserver import ffmpeg
from glassserver import models
from glassserver import infocollecter
from glassserver import mediacache

CHUNK_DURATION = 30
#BASE_URL = "https://192.168.100.10:444/api/"
#BASE_URL = "http://192.168.100.10:1234/api/"
BANDWIDTHS = [800, 1440, 2300, 3000, 4000, 8000]
AR = 16/9

def baseURL():
    return request.url_root + "api/"

@app.route("/glass/test")
def glass():
    return "glass/test"

@app.route("/test")
def root():
    return "test"

@app.route("/api/hls/<int:file_id>/master.m3u8")
def hls_master(file_id):
    buf = "#EXTM3U\n"
    buf += "#EXT-X-VERSION:3\n"
    for b in BANDWIDTHS:
        r = ffmpeg.resolutionMap(b)
        buf += "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH={},RESOLUTION={}x{}\n".format(b, r[0], r[1])
        buf += baseURL() + "hls/{}/index_{}.m3u8\n".format(file_id, b)

    resp = Response(buf, mimetype='application/x-mpegurl')
    resp.headers.extend({"Access-Control-Allow-Origin": "*",
                         "Access-Control-Expose-Headers": "Content-Length"})
    return resp

@app.route("/api/hls/<int:file_id>/index_<int:bandwidth>.m3u8")
def hls_bandwidth(file_id, bandwidth):

    dbFile = models.MediaFile.query.filter_by(id=file_id).first()
    prefix = dbFile.prefix.path
    path = dbFile.path

    print(prefix)

    ospath = prefix + path
    print(ospath)

    #ospath = "/media/hdd1/TV/shows/Doctor-Who/Doctor-Who_S10E00.mp4"

    max_chunk_duration = 60
    #splittimes = ffmpeg.calculate_splittimes(ospath, CHUNK_DURATION)
    splittimes = mediacache.calc_splittimes(ospath, CHUNK_DURATION)

    buf = '#EXTM3U\n'
    buf += '#EXT-X-VERSION:3\n'
    buf += '#EXT-X-TARGETDURATION:{}\n'.format(CHUNK_DURATION)
    buf += '#EXT-X-MEDIA-SEQUENCE:0\n'

    for (pos, chunk_duration) in splittimes:
        buf += "#EXTINF:{},\n".format(chunk_duration)
        buf += baseURL() + "frag/{}/{}_{}_{}.ts\n".format(file_id, bandwidth, pos, chunk_duration)

    buf += '#EXT-X-ENDLIST\n'
    resp = Response(buf, mimetype='application/x-mpegurl')
    resp.headers.extend({"Access-Control-Allow-Origin": "*",
                         "Access-Control-Expose-Headers": "Content-Length"})
    return resp

@app.route('/api/dash/<int:file_id>/manifest.mpd')
def dash_manifest(file_id):
    representations = [{"id": 1, "bandwidth": 800, "height": 720, "width": 1280},
                       {"id": 2, "bandwidth": 2000, "height": 1080, "width": 1920}]

    resp = render_template("manifest.mpd",
                           duration="42S",
                           representations=representations)

    return Response(resp, mimetype="application/octet-stream")

@app.route('/api/frag/<int:file_id>/<int:bandwidth>_<float:ss>_<float:t>.ts')
def frag(ss, t, bandwidth, file_id):
    #path = os.path.normpath(path)
    #ospath = os.path.join(root_directory, path)

    dbFile = models.MediaFile.query.filter_by(id=file_id).first()
    prefix = dbFile.prefix.path
    path = dbFile.path

    print(prefix)

    ospath = prefix + path
    print(ospath)

    #ospath = "/media/hdd1/TV/shows/Doctor-Who/Doctor-Who_S10E00.mp4"

    data = ffprobe(ospath)
    duration = float(data['format']['duration'])
    # cut at next key frame after given time 'ss'
    #_, new_ss = ffmpeg.find_next_keyframe(ospath, ss, t / 2)
    new_ss = ss
    if ss + t * 2 > duration:
        # encode all remain frames at once
        new_t = duration - new_ss
    else:
        # find next key frame after given time 't'
        new_t_prev_duration, new_t = ffmpeg.find_next_keyframe(ospath, ss + t, t / 2)
        new_t -= new_ss
        # minus one frame
        # new_t -= new_t_prev_duration

    process = ffmpeg.stream(ospath, new_ss, new_t, bandwidth)
    resp =  Response(process.stdout, mimetype='video/MP2T')

    #  VERY, VERY IMPORTANT TO AVOID MEMORY OVERFLOW!!!!!!!
    process.kill
    del process
    resp.headers.extend({"Access-Control-Allow-Origin": "*",
                         "Access-Control-Expose-Headers": "Content-Length"})
    return resp

@app.route("/api/importAll")
def importAll():
    infocollecter.importAll()
    return jsonify({"Info": "Started import"})

def ffprobe(path):
    try:
        data = ffmpeg.ffprobe_data(path)
        if 'format' not in data or \
           'duration' not in data['format']:
            logging.warning('analysis failed for %s: Incomplete data', path)
            return None
        else:
            return data
    except:
        print('ffprobe failed for %s', path)
    return None

def generateUrls(id):
    urls = {"hls": baseURL() + "hls/" + str(id) + "/master.m3u8"}
            #"mp4": BASE_URL + "mp4/" + str(id) + ".mp4"}
    return urls


import cv2
import os
import glob
import numpy as np

class Bbox:

	def __init__(self, xmin, ymin, xmax, ymax, cat = None):
		self.xmin = xmin
		self.xmax = xmax
		self.ymin = ymin
		self.ymax = ymax
		self.cat = cat

	def __getitem__(self,key):
		return getattr(self, key)

def convert_to_bbox(labels_all):
	objs = []
	for label in labels_all:
		label = label.split("\n")
		xmin, ymin, xmax, ymax = label[1].split(" ")
		objs.append(Bbox(int(xmin), int(ymin), int(xmax), int(ymax), int(label[0])))

	return objs

def manip_image_and_label(image_file, objs, config):
	image = cv2.imread(image_file)
	h, w, c = image.shape
	image = cv2.resize(image, (config["IMAGE_H"], config["IMAGE_W"]))
	for obj in objs:

		obj.xmin = int(obj.xmin * float(config['IMAGE_W']) / w)
		obj.xmin = max(min(obj.xmin, config['IMAGE_W']), 0)

		obj.xmax = int(obj.xmax * float(config['IMAGE_W']) / w)
		obj.xmax = max(min(obj.xmax, config['IMAGE_W']), 0)

		obj.ymin = int(obj.ymin * float(config['IMAGE_H']) / h)
		obj.ymin = max(min(obj.ymin, config['IMAGE_H']), 0)

		obj.ymax = int(obj.ymax * float(config['IMAGE_H']) / h)
		obj.ymax = max(min(obj.ymax, config['IMAGE_H']), 0)

	return image, objs

def manip_image(image_file, config):
	image = cv2.imread(image_file)
	image = cv2.resize(image, (config["IMAGE_H"], config["IMAGE_W"]))
	return image

def _interval_overlap(interval_a, interval_b):
	x1, x2 = interval_a
	x3, x4 = interval_b

	if x3 < x1:
		if x4 < x1:
			return 0
		else:
			return min(x2,x4) - x1
	else:
		if x2 < x3:
			 return 0
		else:
			return min(x2,x4) - x3

def compute_iou(box1, box2):
	intersect_w = _interval_overlap([box1.xmin, box1.xmax], [box2.xmin, box2.xmax])
	intersect_h = _interval_overlap([box1.ymin, box1.ymax], [box2.ymin, box2.ymax])

	intersect = intersect_w * intersect_h

	w1, h1 = box1.xmax-box1.xmin, box1.ymax-box1.ymin
	w2, h2 = box2.xmax-box2.xmin, box2.ymax-box2.ymin

	union = w1*h1 + w2*h2 - intersect

	return float(intersect) / union

def train_test_split(data_path,*args):
    if len(args) ==0:
        percentage_test = 10
    else:
        percentage_test=args[0]
    img_count = len(os.listdir(data_path+"images"))
    file_train = open(data_path+'train.txt', 'w+')
    file_test = open(data_path+'test.txt', 'w+')
    counter = 1
    index_test = round(100 / percentage_test)
    for cats in os.listdir(os.path.join(data_path,"images/")):
        image_path=os.path.join(data_path,"images/",cats)
        for i in glob.iglob(os.path.join(image_path, "*.jpg")):
            if counter == index_test:
                counter = 1
                file_test.write(i+"\n")
            else:
                file_train.write(i+"\n")
                counter = counter + 1

def resize(img, labels, img_size):
	img_h, img_w = img.shape[:2]
	ratio = max(img_h, img_w) / img_size
	new_h = int(img_h / ratio)
	new_w = int(img_w / ratio)
	ox = (img_size - new_w) // 2
	oy = (img_size - new_h) // 2
	scaled = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_CUBIC)
	out = np.ones((img_size, img_size, 3), dtype=np.uint8) * 127
	out[oy:oy + new_h, ox:ox + new_w, :] = scaled

	scaled_labels = []
	for label in labels:
	    xmin, ymin, xmax, ymax = label[0:4]
	    xmin = (xmin * new_w + ox) / img_size
	    ymin = (ymin * new_h + oy) / img_size
	    xmax = (xmax * new_w + ox) / img_size
	    ymax = (ymax * new_h + oy) / img_size
	    label = [xmin, ymin, xmax, ymax] + label[4:]
	    scaled_labels.append(label)

	return out, scaled_labels


import tweepy
import Tkinter
import random
import threading
import time
from tweepy.streaming import StreamListener

consumer_key = 'CONSUMER_KEY'
consumer_secret = 'CONSUMER_SECRET'
access_token = 'ACCESS_TOKEN'
access_token_secret = 'ACCESS_TOKEN_SECRET'

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)

user = api.me()

def thread_1():
    while True:
        stuff = api.user_timeline(screen_name = 'realDonaldTrump', count = 1, include_rts = True)
        old_tweet_id = open("last_tweet_id.txt", "r")
        for status in stuff:
            with open('last_tweet_id.txt', 'r') as myfile:
                old_tweet_id = myfile.read().replace('\n', '')
                print(old_tweet_id)
                if old_tweet_id == str(status.id):
                    print("no new tweets, taking a 5 minute break")
                    time.sleep(300)
                else:
                    sentence_1 = ["you","you are a","you're a","christ almighty you", "oohhh you", "christ you""mother of god","Jaysus you're a","",]
                    sentence_2 = ["feckin","complete","complete and utter","total","", "absolute", "absolute feckin", "feckin absolute", "feckin complete"]
                    sentence_3 = ["gobshite","numpty","moron","gowal","tik","thick","twat", "dope", "amadan", "simpleton", "feck arse", "silly billy", "dzope", "twonk", "weak man"]
                    print("Found new tweet:'" + status.text + "' with status id: " + str(status.id))
                    print("sending a reply...")
                    tweet = random.choice(sentence_1) + " " + random.choice(sentence_2) + " " + random.choice(sentence_3)
                    print("sending tweet: " + tweet)
                    new_tweet_id = str(status.id)
                    text_file = open("last_tweet_id.txt", "w")
                    text_file.write(new_tweet_id)
                    text_file.close()
                #send tweet
                    api.update_status("@realdonaldtrump " + tweet, in_reply_to_status_id = status.id)

def thread_2():
    while True:
        for mentions in tweepy.Cursor(api.mentions_timeline).items():
            if "#tellhim" in mentions.text.lower():
                if str(mentions.id) not in open('reply_tweet_ids.txt').read():
                    text_file = open("reply_tweet_ids.txt", "a")
                    mention_tweet = str(mentions.id) + "\n"
                    text_file.write(mention_tweet)
                    text_file.close()
                    # read_reply_id = myfile.read().replace('\n', '')
                    reply = ["{handle} oh I'll feckin tell him", "{handle} I'll let him know", "{handle} oh i'll tell him", "don't worry {handle}, he'll hear the words of the irish people"]
                    print("new tweet request")
                    handle = "@" + mentions.user.screen_name
                    response = " " + random.choice(reply).format(handle=handle)
                    print("responding to tweet: " + response)
                    api.update_status(response, in_reply_to_status_id = mentions.id)
                    print("sending trump a tweet...")
                    reply = "@realDonaldTrump" + mentions.text.replace("#tellhim", "").replace("#TellHim", "").replace("@Big_Sean_Murph", "")
                    api.update_status(reply)
                    print(reply)
                else:
                    print("skipping old tweet")
            else:
                print("not a tell him tweet")
        time.sleep(120)

t1 = threading.Thread(target = thread_1)
t2 = threading.Thread(target = thread_2)

t1.start()
t2.start()


import sklearn
import numpy as np
import pandas as pd
from fbprophet import Prophet
import matplotlib.pyplot as plt
from functools import reduce
from sklearn.metrics import mean_absolute_error, mean_squared_error
from fbprophet import Prophet
import plotly.graph_objects as go
import argparse
# Load your cloud drive
# from google.colab import drive
# drive.mount('/content/drive')

def run(train,test,output):
  # 讀入series
  colnames=['Open','High','Low','Close']
  df_temp = pd.read_csv(train, names=colnames)
  date = pd.date_range(start='2015/01/01',periods=len(df_temp))
  df = df_temp
  date = pd.DataFrame(date)
  df[['Date']] = date
  #df.set_index('Date',inplace=True)
  price = df.squeeze()
  price.head()

  # 讀入series
  colnames=['Open','High','Low','Close']
  df_temp = pd.read_csv(test, names=colnames)
  date2 = pd.date_range(start=date.iloc[len(date)-1,0],periods=2)
  date2 = pd.DataFrame(date2)
  date2 = pd.date_range(start=date2.iloc[len(date2)-1,0],periods=len(df_temp))
  date2 = pd.DataFrame(date2)
  df2 = df_temp
  df2[['Date']] = date2
  #df2.set_index('Date',inplace=True)
  price2 = df2.squeeze()
  price2.head()

  df = pd.concat([df,df2])
  df = df.reset_index()
  df = df[['Open','High','Low','Close','Date']]
  df

  def relative_strength_idx(df, n=14):
      Open = df['Open']
      delta = Open.diff()
      delta = delta[1:]
      pricesUp = delta.copy()
      pricesDown = delta.copy()
      pricesUp[pricesUp < 0] = 0
      pricesDown[pricesDown > 0] = 0
      rollUp = pricesUp.rolling(n).mean()
      rollDown = pricesDown.abs().rolling(n).mean()
      rs = rollUp / rollDown
      rsi = 100.0 - (100.0 / (1.0 + rs))
      return rsi

  # SMA
  df['EMA_5'] = df['Open'].ewm(5).mean().shift()
  df['EMA_10'] = df['Open'].ewm(10).mean().shift()
  df['EMA_20'] = df['Open'].ewm(20).mean().shift()

  # EMA
  df['SMA_5'] = df['Open'].rolling(5).mean().shift()
  df['SMA_10'] = df['Open'].rolling(10).mean().shift()
  df['SMA_20'] = df['Open'].rolling(20).mean().shift()

  # RSI
  df['RSI'] = relative_strength_idx(df).fillna(0)

  # MACD
  EMA_12 = pd.Series(df['Open'].ewm(span=12, min_periods=12).mean())
  EMA_26 = pd.Series(df['Open'].ewm(span=26, min_periods=26).mean())
  df['MACD'] = pd.Series(EMA_12 - EMA_26)
  df['MACD_signal'] = pd.Series(df.MACD.ewm(span=9, min_periods=9).mean())

  df['y'] = df['Open'].shift(1)
  df = df.dropna(axis=0).reset_index(drop=True)

  df_train = df.head(len(df)-len(df2))
  df_valid = df.tail(len(df2))

  features = ['SMA_5','SMA_10','SMA_20','EMA_5','EMA_10','EMA_20','RSI','MACD','MACD_signal']
  model_fbp = Prophet()
  for feature in features:
      model_fbp.add_regressor(feature)

  model_fbp.fit(df_train[['Date', 'y'] + features].rename(columns={'Date': 'ds', 'Open': 'y'}))
  forecast = model_fbp.predict(df_valid[['Date', 'Open'] + features].rename(columns={'Date': 'ds'}))
  df_valid["Forecast_Prophet"] = forecast.yhat.values

  df_valid['new_y'] = df_valid['Open']
  df_valid['Forecast_Prophet_move'] = df_valid['Forecast_Prophet'].shift(-1)
  #df_valid[['new_y', 'Forecast_Prophet_move']].plot()

  predict_price = df_valid['Forecast_Prophet_move'].tolist()
  predict_price.insert(0,df_train['Open'][len(df_train)-1])

  #一階
  own = 0
  answer = []
  for i in range(1,len(predict_price)-1):
    if predict_price[i] - predict_price[i-1] > predict_price[i+1] - predict_price[i]:
      if own == 0:
        own -= 1
        answer.append(-1)
      elif own == 1:
        own -= 1
        answer.append(-1)
      elif own == -1:
        answer.append(0)
    if predict_price[i] - predict_price[i-1] < predict_price[i+1] - predict_price[i]:
      if own == 0:
        own += 1
        answer.append(1)
      elif own == 1:
        answer.append(0)
      elif own == -1:
        own += 1
        answer.append(1)
    elif predict_price[i] - predict_price[i-1] == predict_price[i+1] - predict_price[i]:
      answer.append(0)
  answer.append(0)

  csv_file = open(output, "w")
  for i in answer:
    csv_file.write(str(i)+'\n')
  csv_file.close()

# #print("RMSE of Auto ARIMAX:", np.sqrt(mean_squared_error(df_valid.y, df_valid.Forecast_ARIMAX)))
# print("RMSE of Prophet:", np.sqrt(mean_squared_error(df_valid.y, df_valid.Forecast_Prophet)))
# #print("\nMAE of Auto ARIMAX:", mean_absolute_error(df_valid.y, df_valid.Forecast_ARIMAX))
# print("MAE of Prophet:", mean_absolute_error(df_valid.y, df_valid.Forecast_Prophet))

if __name__ == '__main__':
    # You should not modify this part.

    parser = argparse.ArgumentParser()
    parser.add_argument('--training',default='training_data.csv',help='input training data file name')
    parser.add_argument('--testing',default='testing_data.csv',help='input testing data file name')
    parser.add_argument('--output',default='output.csv',help='output file name')
    args = parser.parse_args(args=[])

    run(args.training, args.testing, args.output)


#/bin/python3
# PYTHON VERSION SHOULD BE BIGGER THAN 3.6
import os
import os.path
import sys
from PIL import Image
from PIL import ImageOps
from functools import reduce
import itertools
from tqdm import tqdm

# This flag is setted whenever a function detects an ill-formed (but
# recoverable) input. The flag setting it should also print to the user what
# kind of ill-fomation led to flagging this variable.
compile_issue = False

pngpath = ''
x_start = 0
y_start = 0
script_name = sys.argv[0].rsplit('.', 1)[0]
alpha_color = (240, 240, 232)

def rgb2hex(r, g, b):
    def clamp(x):
        return max(0, min(x, 255))
    return "#{0:02x}{1:02x}{2:02x}".format(clamp(r), clamp(g), clamp(b))

def sort_palette(palette):
    return tuple(sorted(tuple(map(tuple, palette)),
                        key=lambda x: -1 if x == alpha_color else sum(x)))

def yield_image(pngpath):
    img = Image.open(pngpath)
    img = img.convert("RGB")
    return img

def yield_tiledata(pngpath, img_loader=yield_image):
    global x_start
    global y_start

    img = img_loader(pngpath)
    w, h = img.size

    is_divisible_by_8 = lambda x: (x % 8) == 0

    if not is_divisible_by_8(w) or not is_divisible_by_8(h):
        print(f'The file "{pngpath}" isn\'t made of 8x8 tiles, its size is '
              f'{w} ({w % 8} extra pixels) by {h} ({h % 8} extra pixels).')
        compile_issue = True

    for y_start in range(0, h, 8):
        for x_start in range(0, w, 8):
            tilecrop = img.crop([x_start, y_start, x_start + 8, y_start + 8])
            tiledata = tilecrop.load()

            x_start = x_start
            y_start = y_start
            yield tiledata, tilecrop

def extract_palette(tiledata):
        palette = set()

        for x in range(8):
            for y in range(8):
                palette.add(tiledata[x, y])

        return sort_palette(tuple(palette))

def yield_palettes(pngpath):
    for tiledata, tilecrop in yield_tiledata(pngpath):
        palette = extract_palette(tiledata)

        # The GBC (CGB) supports at maximum a 3+1 palette
        if len(palette) > 4:
            print(f'The tile at position {x_start}w,{y_start}h of file '
                  f'{pngpath} has more than 4 colors.')
            compile_issue = True

        # Most PNGs use 8 bits per channel, but CGB uses only 5 bits per
        # channel. We have to be sure that the colors are in 5bpc.
        for color in palette:
            for channel in color:
                if channel % (2 ** (8 - 5)) != 0:
                    print(f'The color {rgb2hex(*color).upper()} '
                          f'[i.e. {str(color)}] at tile '
                          f'{x_start}w,{y_start}h of file {pngpath} '
                          f'isn\'t in the 0RRRRRGG GGGBBBBB format.')
                    compile_issue = True
        yield sort_palette(palette)

artfolder_path = None

# Shows the correct usage in case user misused this script
if len(sys.argv) == 1:
    print(f'    USAGE: {sys.argv[0]} ARTFOLDER_PATH\n')
    exit()
else:
    artfolder_path = sys.argv[1]

print(f'Compiling PNG files in folder "{artfolder_path}" ...')

for (dirpath, dirnames, filenames) in os.walk(artfolder_path):
    pngpaths = [os.path.join(artfolder_path, f)
                for f in filenames
                if f.lower().endswith('.png')]
    pngpaths.sort()
    break

palettes = set()

# We should do a first pass analyzing the images in order to see if the images
# match with the Game Boy Color's display limitations (i.e. 8x8 tiles with 3+1
# palettes). Those checks are cortesy of yield_palettes.
# Aftewards, we join every file palettes into a big list of palettes (excluding
# duplicates).
palettes = set(reduce(lambda x, y: list(x) + list(y),
                      map(yield_palettes, pngpaths)))

if compile_issue is True:
    print("The compilation of the art assets can't proceed if the above "
          "issues aren't fixed.")
    exit()

# Sometimes a tile doesn't uses all colors of it's palette, yet it still uses
# that particular palette. In other words, the tile's palette is a subset of
# another "general" palette.
for palette_a in list(palettes):
    for palette_b in list(palettes):
        if (set(palette_a) < set(palette_b)) and palette_a in palettes:
            palettes.remove(palette_a)

palettes = list(map(sort_palette, palettes))
palettes.sort(key=len)

# We know should gather some data about palette usage in order to build the
# final game's palette bank(s). We also will use this usage data to warn the
# user about potential palette clashes or palette misuse.
histogram = {}
for pngpath in pngpaths:
    for palette in yield_palettes(pngpath):
        palette = set(palette)

        for model_palette in palettes[::-1]:

            # Pads the palette so we ALWAYS have 4 colors
            while len(model_palette) < 4:
                model_palette += (alpha_color, )

            if palette <= set(model_palette):
                histogram[model_palette] = (histogram.get(model_palette, [])
                                            + [(pngpath, x_start, y_start)])
                break

histogram = sorted(histogram.items(), key=lambda x: len(x[-1]), reverse=True)
final_palettes = [pal for pal, occurs in histogram]

# Outputs a palette overview for the user AND a detailed description of the
# palettes.
with open(script_name + '.log', 'w') as logfile:
    print('The following palettes were found:\n')
    for index, (palette, occurs) in enumerate(histogram):
        colors_str = ", ".join(map(lambda x: rgb2hex(*x), palette))
        header_str = f'#{index}: {colors_str:<36} -- {len(occurs):>5} usages'
        print(' ' * 4 + header_str)
        logfile.write(header_str + '\n')

        for (pngpath, x_start, y_start) in occurs:
            logfile.write(' ' * 4
                          + f'tile at {x_start:>3}w, {y_start:>3}h '
                          f'of file {pngpath}\n')

        logfile.write('\n\n\n')

print('')

# With all the usage data we finally can output a nice PNG art representing the
# game's palette banks.
palette_histogram = dict([(k, len(v)) for k, v in histogram])
del histogram

palette_image = Image.new('RGB', (4, len(final_palettes)))
palette_image_data = palette_image.load()
for h in range(len(final_palettes)):
    for w in range(4):
        palette_image_data[w, h] = final_palettes[h][w]

palette_image.save('palettes_overview_log.png')

#
# With all the palette analysis done, we finally can analyze the tiles.
#

def to_final_palette(palette):
    for final_palette in final_palettes:
        if set(palette) <= set(final_palette):
            return final_palette

def indexed_tiledata(tiledata, palette):
    acc = []

    for y in range(8):
        for x in range(8):
            acc += [palette.index(tiledata[x, y])]
    return acc

vflip = lambda x: ImageOps.flip(x)
hflip = lambda x: ImageOps.mirror(x)
dflip = lambda x: vflip(hflip(x))
noflip = lambda x: x

tiles = {}
tiles_order = {}
usual_tile_palette = {}

def to_final_tile(tilecrop, final_palette):
    for indexed_final_tile in sorted(tiles.keys(),
                                     key=lambda x: tiles[x],
                                     reverse=True):
        for mapper in [noflip, hflip, vflip, dflip]:
            indexed_tile = tuple(indexed_tiledata(mapper(tilecrop).load(), final_palette))
            if indexed_final_tile == indexed_tile:
                return indexed_final_tile, mapper

    return None, None

print("Compressing tiles into tilemap, please wait...\n")
tiles_seen = 0
total = len([1 for x, y in itertools.chain(*map(yield_tiledata, pngpaths))])
for tiledata, tilecrop in tqdm(itertools.chain(*map(yield_tiledata, pngpaths)),
                               total=total):
    palette = extract_palette(tiledata)
    palette = to_final_palette(palette)

    indexed_final_tile, fliptype = to_final_tile(tilecrop, palette)

    if indexed_final_tile is not None:
        tiles[indexed_final_tile] += 1
    else:
        indexed_tile = tuple(indexed_tiledata(tiledata, palette))
        tiles[indexed_tile] = tiles.get(indexed_tile, 0) + 1
        usual_tile_palette[indexed_tile] = palette
        tiles_order[indexed_tile] = tiles_seen
        tiles_seen += 1

print("")
print(f"We found {len(tiles.keys())} tiles, used across {sum(tiles.values())}"
      " occurrences.")

def convert_tile_to_ascii(tile):
    k = tile
    k = list(k)
    m2 = {0: ' ', 3: '▓', 2: '▒', 1:'░'}
    m = {0: ' ', 3: 'M', 2: ':', 1:'.'}
    tilestr = ""
    for l in range(0, 64, 8):
         tilestr +=('\n'
                    + "".join(map(lambda x: m[x], k[l:l+8]))
                    + "."
                    + "".join(map(lambda x: m2[x], k[l:l+8])))
    return tilestr

def output_tiles(tilelist, pngpath):
    tiles = tilelist
    side = int(len(tiles)**0.5)+1
    tilemap_image = Image.new('RGB', (side * 8, side * 8))
    tilemap_data = tilemap_image.load()

    x = 0
    y = 0
    for tile in tiles:
        palette = usual_tile_palette[tile]
        color_guide = dict(enumerate(palette))

        tile = [color_guide[x] for x in tile]

        for h in range(8):
            for w in range(8):
                tilemap_data[x + w, y + h] = tile[h * 8 + w]

        x += 8
        if x >= (side * 8):
            x -= (side * 8)
            y += 8

    tilemap_image.save(pngpath)

output_tiles(sorted(tiles.keys(), key=lambda x: tiles[x], reverse=True),
             'tilemap_usage_order_log.png')

output_tiles(sorted(tiles.keys(), key=lambda x: tiles_order[x]),
             'tilemap_overview_log.png')

asmfile = open(os.path.join(artfolder_path, 'compiled_artwork.asm'), 'w')

def as_safe_str(part):
    return ''.join([x if x.isalnum() else '_' for x in part]).strip()

for part in artfolder_path.split('/')[::-1]:
    part = as_safe_str(part)
    length = len(''.join([x for x in part if x.isalnum()]).strip())

    if length > 0:
        prefix = part.strip()
        break

asmprint = lambda x: asmfile.write(x)

asmprint(f'''; THIS FILE WAS AUTOGENERATED BY {script_name}
; It should contain the graphic's data for folder {artfolder_path}
; The unique prefix for these graphics is "{prefix}"

section "__compiled_artwork__{prefix}__", ROM0

CG_PaletteBank__{prefix}:
''')

indent = '''                '''

fmt = '''

;{indent}This palette #{number} occured in {occurs} tiles
{palettelabel}:
'''

def palette_label(final_palette):
    global prefix
    number = final_palettes.index(final_palette)
    return f'CG_Palette__{prefix}__{number}'

fmtcolor = ''';{indent}HEX: {hexcolor} RGB: {r:>3} {g:>3} {b:>3}
;{indent}    xBbBbBGgGgGRrRrR
{indent} dw %0{bbin}{gbin}{rbin}

'''
for number, palette in enumerate(final_palettes):
    palettelabel = palette_label(palette)
    occurs = palette_histogram[palette]
    asmprint(fmt.format(**locals()))

    for r, g, b in palette:
        hexcolor = rgb2hex(r, g, b).upper()
        gbc_bin = lambda x: "{0:08b}".format(x)[:-3]
        rbin = gbc_bin(r)
        gbin = gbc_bin(g)
        bbin = gbc_bin(b)

        asmprint(fmtcolor.format(**locals()))

def tile_label(indexed_tile):
    global prefix
    number = tiles_order[indexed_tile]
    return f'CG_TILE__{prefix}__{number}'

fmt = '''

;{indent} TILE #{number} -- used {usage} times
;
{tileascii}
{tilelabel}:
;{indent}    abcdefgh,  ABCDEFGH
'''
fmtline = '''{indent} db %{lowerbyte}, %{higherbyte}
'''
for number, tile in enumerate(sorted(tiles.keys(),
                                     key=lambda x: tiles_order[x])):
    number = tiles_order[tile]
    tilelabel = tile_label(tile)
    usage = tiles[tile]
    tileascii = (convert_tile_to_ascii(tile)
                 .replace('\n',f'.\n;{indent}.')
                 .lstrip()
                 .lstrip(f'.\n{indent}') + '.')
    asmprint(fmt.format(**locals()))

    hb = {0: '0', 1: '0', 2: '1', 3: '1'}
    lb = {0: '0', 1: '1', 2: '0', 3: '1'}

    for l in range(0, 64, 8):
        line = tile[l:l+8]
        higherbyte = ''.join(map(lambda x: hb[x], line))
        lowerbyte = ''.join(map(lambda x: lb[x], line))
        asmprint(fmtline.format(**locals()))

fmt = '''

;{indent}Sprite of file {spritepath}
;{indent}Size: {w}px x {h}px, or {tw} tiles x {th} tiles
CG_SPRITE__{prefix}__{sprite_prefix}:
.length:{length_indent} dw {length}
.size:
.size_x:{size_indent} db {tw}
.size_y:{size_indent} db {th}
.tiledata:
'''

fmt_tile = '''.tile_x{xt_start:02}_y{yt_start:02}:{tileindent} dw {tilelabel}
'''

fmt_metadata_header = '''; Metadata for this tile, PPP=Palette n., B=bank, p=priority
;{indent}    pVH0BPPP
.metadata:
'''

fmt_tile_metadata = ('''.meta_x{xt_start:02}_y{yt_start:02}:'''
                     '''{metaindent} db %0{flipmodebin}00{palettebin} ; {tile_desc:<23} on {tilelabel}
''')

str_tile = ''
str_metadata = ''

print("\nCompiling sprite data, please wait...\n")
for spritepath in tqdm(pngpaths):
    sprite_prefix = as_safe_str(os.path.splitext(os.path.basename(spritepath))[0])
    img = yield_image(spritepath)
    w, h = img.size
    tw, th = w // 8, h // 8
    length = tw * th
    size_indent = ' ' * (len(indent) - len('.size_x:'))
    tileindent = ' ' * (len(indent) - len('.tile_x00_y00:'))
    metaindent = ' ' * (len(indent) - len('.meta_x00_y00:'))
    length_indent = ' ' * (len(indent) - len('.length:'))

    asmprint(fmt.format(**locals()))

    for tiledata, tilecrop in yield_tiledata(spritepath):
        palette = to_final_palette(extract_palette(tiledata))
        final_tile, flipmode = to_final_tile(tilecrop, palette)

        palettelabel = palette_label(palette)
        tilelabel = tile_label(final_tile)

        flipmapper = {noflip: '00', hflip: '01', vflip: '10', dflip: '11'}
        flipstr = {noflip: 'no mirror', hflip: 'Mirror Horiz.',
                   vflip: 'Mirror Vert.', dflip: 'Mirror H. & V.'}

        flipmodebin = flipmapper[flipmode]
        palette_num = final_palettes.index(palette)
        palettebin = '{:03b}'.format(palette_num)

        tile_desc = 'PAL #{}, {}'.format(palette_num, flipstr[flipmode])
        xt_start = x_start // 8
        yt_start = y_start // 8

        str_tile += fmt_tile.format(**locals())
        str_metadata += fmt_tile_metadata.format(**locals())

    asmprint(str_tile)
    asmprint(fmt_metadata_header.format(**locals()))
    asmprint(str_metadata)

    str_tile = ''
    str_metadata = ''

asmfile.close()


class Solution(object):
    def reorderList(self, head):
        """
        :type head: ListNode
        :rtype: None Do not return anything, modify head in-place instead.
        """

        slow = fast = head

        # 找中点，slow 最后指向中点
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next

        # 逆序中点后面节点
        curr = slow.next
        slow.next = None
        prev = None

        while curr:
            next = curr.next
            curr.next = prev
            prev = curr
            curr = next

        beg = head
        end = prev

        while beg and end:
            next1 = beg.next
            next2 = end.next

            beg.next = end
            end.next = next1

            beg = next1
            end = next2


#!/usr/bin/python
import datetime
import sqlite3
import time
import paho.mqtt.publish as publish
import os

sqlite_file = '/home/pi/.KilliFeeder/data/db.sqlite'

table_name = 'clients'   # name of the table to be created
id_column = 'client_id' # name of the PRIMARY KEY column
order_column = 'orders'  # name of the new column
senspt_column = 'senspt'
cal_column = 'calibrated'  # name of the new column

################################################################################
# Connecting and creating the database file #
################################################################################
if (not os.path.isfile(sqlite_file)):
    default_val_ord = ",".join(map(str,[434*60, 614*60, 734*60, 1094*60])) # a default value for the new column rows
    default_val_cal = 'no' # a default value for the new column rows
    default_val_spt = '88'
    column_type = 'TEXT' # E.g., INTEGER, TEXT, NULL, REAL, BLOB
    conn = sqlite3.connect(sqlite_file)
    c = conn.cursor()
    # Creating a second table with 1 column and set it as PRIMARY KEY
    # note that PRIMARY KEY column must consist of unique values!
    c.execute('CREATE TABLE {tn} ({nf} {ft} PRIMARY KEY)'\
            .format(tn=table_name, nf=id_column, ft=column_type))

    c.execute("ALTER TABLE {tn} ADD COLUMN '{cn}' {ct} DEFAULT '{df}'"\
            .format(tn=table_name, cn=order_column, ct=column_type, df=default_val_ord))

    c.execute("ALTER TABLE {tn} ADD COLUMN '{cn}' {ct} DEFAULT '{df}'"\
            .format(tn=table_name, cn=cal_column, ct=column_type, df=default_val_cal))

    c.execute("ALTER TABLE {tn} ADD COLUMN '{cn}' {ct} DEFAULT '{df}'"\
            .format(tn=table_name, cn=senspt_column, ct=column_type, df=default_val_spt))
    # Committing changes and closing the connection to the database file
    conn.commit()
    conn.close()
################################################################################

### need to also publish "orders" for all client ids, reference csv,
### setup new topic if new client with defaults and calibrated flag = "no"

###  initialize counter and client_list
i = 0
def getclients_orders():
    ordersdict = {}
    caldict = {}
    sensptdict = {}
    client_list = []
    conn = sqlite3.connect(sqlite_file)
    c = conn.cursor()
    c.execute('SELECT {coi},{coi2},{coi3},{coi4} FROM {tn}'.\
            format(coi=id_column, coi2=order_column, coi3=cal_column, coi4=senspt_column,tn=table_name))
    all_rows = c.fetchall()
    conn.commit()
    conn.close()
    for i in all_rows:
        client_list.append(str(i[0]))
        ordersdict[str(i[0])] = str(i[1])
        caldict[str(i[0])] = str(i[2])
        sensptdict[str(i[0])] = str(i[3])

    return client_list,ordersdict,caldict,sensptdict

### initialize client_list
client_list,ordersdict,caldict,sensptdict = getclients_orders()
print(sensptdict)
print(client_list)
### publish time and orders
i = 0
while True:
    now = datetime.datetime.now()
    secondtime = (now.second)+(now.minute*60)+(now.hour*60*60)
    publish.single("time", payload=secondtime, qos=0, retain=True) # was False
    print(secondtime)
    print(i)
    ### every 1000 iterations, check the csv file and look for new client_ids to publish to:
    client_list,ordersdict,caldict,sensptdict = getclients_orders()
    ### loop through and publish orders for all clients
    if i > 5:
        i = 0
        print(client_list)
        for client_id in client_list:
            print(client_id)
            topic = str("orders/"+client_id)
            specificorders =  ",".join(map(str,[ordersdict[client_id],caldict[client_id],sensptdict[client_id]]))
            print(specificorders)
            publish.single(topic, payload=specificorders, qos=0, retain=True) # was False

    print("repeat")
    i= i+1
    time.sleep(1)


"""Wrapper around texts to work best as an agent."""
from langchain.agents.tools import BaseTool

import json
from typing import Any, Coroutine

from jeeves import texts

def create_text_message_tool(inbound_phone: str) -> type[BaseTool]:
    """
    Create a tool to send text messages.

    Args:
        inbound_phone: The phone number to send a confirmation text to after
            sending the message.

    Returns:
        A tool to send text messages.
    """
    class TextMessageTool(BaseTool):
        """Wrapper around texts to work best as an agent."""
        name: str = "Send Text Message"
        description = (
            "Useful for when you need to send a text message. Input must be a JSON string with "
            'the keys "content" and "recipient_phone" (10-digit phone number preceded by '
            'country code, ex. "12223334455". Do not make up phone numbers - either '
            "use a phone number explicitly provided by the user, or use a phone number from a "
            "tool that provides it for you (ex. contacts, if available). Otherwise, do not use this tool. "
            'Write the content as you, Jeeves, not as me.'
        )

        def _run(self, query: str) -> str:
            """Send a text message."""
            input_parsed = json.loads(query)

            # Validate
            assert "content" in input_parsed, 'Input must have a "content" key.'
            assert isinstance(input_parsed["content"], str), "Content must be a string."
            content = input_parsed["content"]

            assert (
                "recipient_phone" in input_parsed
            ), 'Input must have a "recipient_phone" key.'
            assert (
                len(str(input_parsed["recipient_phone"]).replace("+", "")) == 11
            ), "Recipient must be a phone number preceded by country code."
            recipient = str(input_parsed["recipient_phone"])

            try:
                send_res = texts.send_message(content=content, recipient=recipient)
            except Exception as e:
                return f"Error: {str(e)}"
            else:
                texts.send_message(
                    content=(
                        "Sir, I'm informing you that I have sent the following message to "
                        f"{recipient}:\n\n{content}"
                    ),
                    recipient=inbound_phone,
                )

            if send_res:
                return "Message delivered successfully."
            else:
                return "Message failed to deliver."

        def _arun(self, *args: Any, **kwargs: Any) -> Coroutine[Any, Any, str]:
            raise NotImplementedError(f"{type(self).__name__} does not support async.")

    return TextMessageTool


import cv2
import numpy as np

def stackImages(scale,imgArray):
    rows = len(imgArray)
    cols = len(imgArray[0])
    rowsAvailable = isinstance(imgArray[0], list)
    width = imgArray[0][0].shape[1]
    height = imgArray[0][0].shape[0]
    if rowsAvailable:
        for x in range ( 0, rows):
            for y in range(0, cols):
                if imgArray[x][y].shape[:2] == imgArray[0][0].shape [:2]:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
                else:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]), None, scale, scale)
                if len(imgArray[x][y].shape) == 2: imgArray[x][y]= cv2.cvtColor( imgArray[x][y], cv2.COLOR_GRAY2BGR)
        imageBlank = np.zeros((height, width, 3), np.uint8)
        hor = [imageBlank]*rows
        hor_con = [imageBlank]*rows
        for x in range(0, rows):
            hor[x] = np.hstack(imgArray[x])
        ver = np.vstack(hor)
    else:
        for x in range(0, rows):
            if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
                imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
            else:
                imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None,scale, scale)
            if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
        hor= np.hstack(imgArray)
        ver = hor
    return ver

def getContours(img):
    contours,hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
    for cnt in contours:
        area = cv2.contourArea(cnt)
        print(area)
        if area>500:
            cv2.drawContours(imgContour, cnt, -1, (255, 0, 0), 3)
            peri = cv2.arcLength(cnt,True)
            #print(peri)
            approx = cv2.approxPolyDP(cnt,0.02*peri,True)
            print(len(approx))
            objCor = len(approx)
            x, y, w, h = cv2.boundingRect(approx)

            if objCor ==3: objectType ="Tri"
            elif objCor == 4:
                aspRatio = w/float(h)
                if aspRatio >0.98 and aspRatio <1.03: objectType= "Square"
                else:objectType="Rectangle"
            elif objCor>4: objectType= "Circles"
            else:objectType="None"

            cv2.rectangle(imgContour,(x,y),(x+w,y+h),(0,255,0),2)
            cv2.putText(imgContour,objectType,
                        (x+(w//2)-10,y+(h//2)-10),cv2.FONT_HERSHEY_COMPLEX,0.7,
                        (0,0,0),2)

path = 'Resources/shapes.png'
img = cv2.imread(path)
imgContour = img.copy()

imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
imgBlur = cv2.GaussianBlur(imgGray,(7,7),1)
imgCanny = cv2.Canny(imgBlur,50,50)
getContours(imgCanny)

imgBlank = np.zeros_like(img)
imgStack = stackImages(0.8,([img,imgGray,imgBlur],
                            [imgCanny,imgContour,imgBlank]))

cv2.imshow("Stack", imgStack)

cv2.waitKey(0)


from Player import Player
import PIL
from Rules import Rules
import tkinter
from Board import Game

class NewGame:
    def __init__(self):
        self.players = {} #Player dictionary.
        self.total = 1 # Keeps count of the number of active players.
        self.rules = Rules()

    #This function modifys the Rules class with the input of the user.
    def rule_change(self):

        rulescreen = tkinter.Tk()
        rulescreen.title("MyScrabble")
        rulescreen.geometry('400x250+700+400')
        rulescreen.configure(background='#0F0268')

        def rule_confirm():
            self.rules.set_board_size(boardsize.get())
            self.rules.set_turn_time_limit(turntime.get())
            self.rules.set_game_time_limit(gametime.get())
            self.rules.set_rack_size(racksize.get())
            print(self.rules.get_details())
            rulescreen.destroy()

        tkinter.Label(rulescreen, text="Board Size:", bg='#0F0268', fg='white', font=("Segoe UI Bold", 13)).place(x=40,y=40)
        tkinter.Label(rulescreen, text="Turn Time:", bg='#0F0268', fg='white', font=("Segoe UI Bold", 13)).place(x=40,y=70)
        tkinter.Label(rulescreen, text="Game Time:", bg='#0F0268', fg='white', font=("Segoe UI Bold", 13)).place(x=40,y=100)
        tkinter.Label(rulescreen, text="Rack Size:", bg='#0F0268', fg='white', font=("Segoe UI Bold", 13)).place(x=40,y=130)
        boardsize = tkinter.Entry(rulescreen)
        boardsize.place(x=240,y=40)
        boardsize.insert(0, self.rules.board_size)
        turntime = tkinter.Entry(rulescreen)
        turntime.place(x=240,y=70)
        turntime.insert(0, self.rules.turn_time_limit)
        gametime = tkinter.Entry(rulescreen)
        gametime.place(x=240,y=100)
        gametime.insert(0, self.rules.game_time_limit)
        racksize = tkinter.Entry(rulescreen)
        racksize.place(x=240,y=130)
        racksize.insert(0, self.rules.rack_size)
        tkinter.Button(rulescreen, font=("Segoe UI Bold", 13), borderwidth=5, text="Confirm Rules", width=15, height=1,  command=rule_confirm).place(x=120, y=180)
        rulescreen.mainloop()

    def play_online(self):
        pass

    def play_local(self):

        homescreen.destroy()
        setup = tkinter.Tk()
        setup.title("MyScrabble")
        setup.geometry('450x600+720+200')
        setup.configure(background='#0F0268')

        def player_confirm():
            userlist = users.get().split(",")
            usercolours = colours.get().split(",")
            for x in range(0, len(userlist)):
                self.add_player(userlist[x], usercolours[x], "English", x+1)

            for y in range(1, len(userlist)+1):
                print(self.players[y].get_all())
            #######Commands to jump to board go here#####
            scrabble = Game()
            scrabble.run()
            setup.destroy()
        pixelVirtual = tkinter.PhotoImage(width=1, height=1)
        tkinter.Label(setup, bg="#C82323", image=pixelVirtual, width=303, height=88, compound="c").place(x=72, y=48)
        tkinter.Label(setup, text="MyScrabble", font=("Segoe UI Bold", 38), bg="#FF1717", fg="white", image=pixelVirtual, width=293, height=78, compound="c").place(x=76, y=52)
        tkinter.Label(setup, text="Player Names:", bg="#0F0268", fg="white", image=pixelVirtual, width=174, height=33, compound="c", font=("Segoe UI Bold", 20)).place(x=47,y=184)
        tkinter.Label(setup, text="Player Colours:", bg="#0F0268", fg="white", image=pixelVirtual, width=190, height=33, compound="c", font=("Segoe UI Bold", 20)).place(x=47,y=256)
        tkinter.Button(setup, font=("Segoe UI Bold", 13), borderwidth=5, text="Edit Rules",image=pixelVirtual, width=119, height=25, compound="c",  command=test.rule_change).place(x=163, y=455)

        users = tkinter.Entry(setup, width=30)
        users.place(x=240,y=200)
        colours = tkinter.Entry(setup, width=28)
        colours.place(x=250, y=270)
        tkinter.Button(setup, font=("Segoe UI Bold", 25), image=pixelVirtual, borderwidth=5, height=68, width=201, text="Start Game", compound="c", command = player_confirm).place(x=121, y=362)
        setup.mainloop()

    # Creates a new player and adds it to the player dictionary.
    def add_player(self, name, player_colour, language, turn):
        new_player = Player(name, player_colour, language, turn) # Creates an object for a player.
        self.players[self.total] = new_player # adds to the player_dict which is a dictionary.
        self.total = self.total + 1 # Increments the player count by one.
        return

###################################################################################################
test = NewGame()

homescreen = tkinter.Tk()
homescreen.title("MyScrabble")
homescreen.geometry('400x600+720+200')
homescreen.configure(background='#0F0268')
pixelVirtual = tkinter.PhotoImage(width=1, height=1)

tkinter.Label(homescreen, bg="#C82323", image=pixelVirtual, width=303, height=88, compound="c").place(x=43, y=48)
tkinter.Label(homescreen, text="MyScrabble", font=("Segoe UI Bold", 38), bg="#FF1717", fg="white", image=pixelVirtual, width=293, height=78, compound="c").place(x=47, y=52)

tkinter.Button(homescreen, font=("Segoe UI Bold", 20), text="Play Online", image=pixelVirtual, borderwidth=5, width=172, height=59, compound="c", command=test.play_local).place(x=102, y=181)
tkinter.Button(homescreen, font=("Segoe UI Bold", 20), text="Play Offline", borderwidth=5,image=pixelVirtual, width=172, height=59, compound="c",  command=test.play_local).place(x=102, y=266)
tkinter.Button(homescreen, font=("Segoe UI Bold", 20), text="Log Out",image=pixelVirtual, borderwidth=5, width=172, height=59, compound="c",  command=test.play_local).place(x=102, y=350)
tkinter.Button(homescreen, font=("Segoe UI Bold", 11), text="Contact Admin",image=pixelVirtual, borderwidth=5, width=129, height=35, compound="c",  command=test.play_local).place(x=129, y=437)
homescreen.mainloop()

Year 3/CA314 OO Analysis and Design/src/run.py


""" Display dialog window about layout """

# Author: Roberto Buelvas

from wx.lib.scrolledpanel import ScrolledPanel
import wx

class LayoutDialog(wx.Dialog):
    """ Class to define dialog window

    Settings in this dialog are of 2 types:
    Distances: Starting with D e.g. DL1, DGLX
    Indexes: Starting with I e.g. IEL
    In particular, the indexes are used with the environmental sensors
    as they are mounted alongside a multispectral sensor, so it is useful
    to tell the distance from that sensor rather than from the common point
    DL1 measures horizontal distance from the first sensing unit on the left to
        the middle point of the platform. It is used for both the multispectral
        and ultrasonic sensors, as they are supposed to be aligned
    DL2 measures horizontal distance from the second sensing unit on the left
        to the first sensing unit on the left
    DL3, ..., DL{n} follow the same pattern measuring from the previous
        sensing unit
    DR1, ..., DR{n} follow the same pattern but for sensing units on the right
    DB1 measures vertical distance from the row of ultrasonic sensors to the
        toolbar the holds the sensors
    DB2 measures vertical distance from the row of multispectral sensors to the
        row of ultrasonic sensors
    DGLX measures horizontal distance from the location of the GPS antenna on
        the left to the middle point of the platform
    DGLY measures horizontal distance from the location of the GPS antenna on
        the left to the middle point of the platform
    DGRX, DGRY are equivalent to DGLX and DGLY respectively, but on the right
    IEL indicates to which multispectral sensor the environmental sensor of the
        left is linked
    IER indicates the same but for the sensor on the right
    DEL measures horizontal distance from the environmental sensor on the left
        to the multispectral sensor linked to it
    DER is equivalent to DEL but for the sensor on the right
    For reference of what is horizontal or vertical, see the schematic image
    in this dialog
    """

    def __init__(self, parent, settings, *args, **kw):
        """ Create new dialog """
        super(LayoutDialog, self).__init__(parent, *args, **kw)
        self.settings = settings
        self.initUI()
        self.SetSize((650, 500))
        self.Centre()
        self.SetTitle("Layout")

    def initUI(self):
        """ Define dialog elements """
        num_sensors = self.settings.ReadInt("numSensors", 1)

        vbox0 = wx.BoxSizer(wx.VERTICAL)
        pnl = ScrolledPanel(self)
        vbox1 = wx.BoxSizer(wx.VERTICAL)

        st = wx.StaticText(
            pnl,
            label="If the sensor numbers don't match, "
            + "make first the adjustments in "
            + "Settings>Ports",
            size=(600, 60),
        )
        st.SetFont(wx.Font(18, wx.DEFAULT, wx.NORMAL, wx.NORMAL))
        vbox1.Add(st, proportion=0, flag=wx.ALL)

        image = wx.Image("docs/diagram.png", wx.BITMAP_TYPE_ANY).Rescale(450, 300)
        imageBitmap = wx.StaticBitmap(pnl, bitmap=wx.Bitmap(image))
        vbox1.Add(imageBitmap, proportion=1, flag=wx.ALL | wx.CENTER)

        st = wx.StaticText(pnl, label="All distances in cm")
        vbox1.Add(st, proportion=0, flag=wx.ALL)

        self.setting_to_control = {}

        self.addLabelledCtrl(pnl, vbox1, "DB1")
        self.addLabelledCtrl(pnl, vbox1, "DB2")
        for i in range(num_sensors):
            self.addLabelledCtrl(pnl, vbox1, "DL" + str(i + 1))
        for i in range(num_sensors):
            self.addLabelledCtrl(pnl, vbox1, "DR" + str(i + 1))
        if self.settings.ReadBool("connectedgL", False):
            self.addLabelledCtrl(pnl, vbox1, "DGLX")
            self.addLabelledCtrl(pnl, vbox1, "DGLY")
        if self.settings.ReadBool("connectedgR", False):
            self.addLabelledCtrl(pnl, vbox1, "DGRX")
            self.addLabelledCtrl(pnl, vbox1, "DGRY")
        if self.settings.ReadBool("connectedeL", False):
            self.addLabelledCtrl(pnl, vbox1, "IEL")
            self.addLabelledCtrl(pnl, vbox1, "DEL")
        if self.settings.ReadBool("connectedeR", False):
            self.addLabelledCtrl(pnl, vbox1, "IER")
            self.addLabelledCtrl(pnl, vbox1, "DER")

        pnl.SetSizer(vbox1)
        pnl.SetupScrolling()

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        okButton = wx.Button(self, label="OK")
        cancelButton = wx.Button(self, label="Cancel")
        hbox.Add(okButton)
        hbox.Add(cancelButton, flag=wx.LEFT, border=5)
        okButton.Bind(wx.EVT_BUTTON, self.OnOK)
        cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)

        vbox0.Add(pnl, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)
        vbox0.Add(hbox, proportion=0, flag=wx.ALIGN_CENTER | wx.ALL, border=10)
        self.SetSizer(vbox0)

    def OnOK(self, e):
        """ Save new settings and close """
        other_settings = list(self.setting_to_control.keys())
        for setting in other_settings:
            ctrl = self.setting_to_control[setting]
            if setting[0] == "D":
                self.settings.WriteFloat(setting, ctrl.GetValue())
            else:
                if setting[0] == "I":
                    self.settings.WriteInt(setting, ctrl.GetValue())
        self.EndModal(wx.ID_OK)

    def OnCancel(self, e):
        """ Do nothing and close """
        self.EndModal(wx.ID_CANCEL)

    def getSettings(self):
        """ Return settings """
        return self.settings

    def getSettingsList(self):
        """ Return keys of settings modified in this dialog """
        return list(self.setting_to_control.keys())

    def addLabelledCtrl(self, panel, boxSizer, label):
        """ Add a StaticText and SpinControl pair

        Args:
            boxSizer (wx.BoxSizer): BoxSizer that will hold everything created
                                    in this function
            pnl (wx.Panel): Panel to be parent of the elements created, since
                            BoxSizers cannot be used as parent
            label (str): Label of the format DGLX or DL1 to help identify which
                         setting is modified by each control
        Besides creating the mentioned controls, the function adds them to the
        attribute setting_to_control for later use.
        This dictionary allows to remember which setting is modified by each
        control.
        Each control is stacked on top of each other with StaticText in between
        to identify them
        """
        st = wx.StaticText(panel, label=label)
        boxSizer.Add(st, proportion=0, flag=wx.CENTER | wx.TOP, border=10)
        if label[0] == "D":
            spinCtrl = wx.SpinCtrlDouble(
                panel, min=-3000, max=3000, initial=self.settings.ReadFloat(label)
            )
            spinCtrl.SetDigits(2)
        else:
            if label[0] == "I":
                spinCtrl = wx.SpinCtrl(
                    panel,
                    min=1,
                    max=self.settings.ReadInt("numSensors", 1),
                    initial=self.settings.ReadInt(label),
                )
        boxSizer.Add(spinCtrl, proportion=0, flag=wx.ALL | wx.CENTER)
        self.setting_to_control[st.GetLabelText()] = spinCtrl


import time
import datetime
import requests
import smtplib
from bs4 import BeautifulSoup
import csv
import pandas as pd

def price_check():
    url = 'https://www.nike.com/t/dunk-high-womens-shoes-L3Tqlr/DD1869-103?nikemt=true&cp=38647199687_search_%7CPRODUCT_GROUP%7CGOOGLE%7C71700000101429394%7CGG_Evergreen_Shopping_Shoes_AllShoes%7C%7Cc&exp=2011&gclid=CjwKCAjwp6CkBhB_EiwAlQVyxXfFXjmymed9WWW2BC6PvU-fqa9_ycDBXt7Juz3fO39eV9mVTVvs8RoCKtgQAvD_BwE&gclsrc=aw.ds'

    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 OPR/99.0.0.0"
    }

    page = requests.get(url, headers=headers)

    content = BeautifulSoup(page.content, 'html.parser')
    prettyContent = BeautifulSoup(content.prettify(), 'html.parser')

    title = prettyContent.find(id='pdp_product_title').get_text()
    price = prettyContent.find("div", {"data-test": "product-price"}).get_text()

    today = datetime.date.today()

    price = price.strip()[1:]
    title = title.strip()

    data = [title, price, today]

    with open('NikeScraper.csv', 'a+', newline='', encoding='UTF8') as outfile:
        writer = csv.writer(outfile)
        writer.writerow(data)

    if int(price) < 130:
        send_gmail()

def create_file():
    header = ['Title', 'Price', 'Date']

    with open('NikeScraper.csv', 'w', newline='', encoding='UTF8') as outfile:
        writer = csv.writer(outfile)
        writer.writerow(header)

def send_gmail():
    s = smtplib.SMTP('smtp.gmail.com', 587)

    s.starttls()

    # info changed for privacy reasons
    s.login("sender_email", "password")

    subject = 'Price dropped!'

    body = 'You should buy now!'

    msg = f"Subject: {subject}\n\n{body}"

    # info changed for privacy reasons
    s.sendmail("sender_email", "receiver_email", msg)

    s.quit()

def check_file():
    df = pd.read_csv(r'C:\Users\maryg\NikeScraper.csv')
    print(df)

if __name__ == '__main__':
    # comment this out if you need to run the program more than once
    # otherwise it will create a new csv and overwrite the existing one
    create_file()

    # use if you need to check what the file currently looks like
    # otherwise can be commented out
    check_file()

    while True:
        price_check()
        time.sleep(86400)