# -*- coding: utf-8 -*-

import arcade
import pymunk
import timeit
import math
import os

LargeurEcran = 1920
HauteurEcran = 1080
Titre = "Simulateur de gravité"

#Définition des formes physiques
class Physique(arcade.Sprite):
    def __init__(self, pymunk_shape, filename):
        super().__init__(filename, center_x=pymunk_shape.body.position.x, center_y=pymunk_shape.body.position.y)
        self.pymunk_shape = pymunk_shape

#Définition des caractéristiques des formes physiques
class Formes(Physique):
    def __init__(self, pymunk_shape, filename, width, height):
        super().__init__(pymunk_shape, filename)
        self.width = width
        self.height = height

class Simulation(arcade.Window):
    #Définition de la simulation

    #Initialsiation
    def __init__(self, width, height, title):
        super().__init__(width, height, title)

        file_path = os.path.dirname(os.path.abspath(__file__))
        os.chdir(file_path)

        # Définition de l'image de fond à l'ouverture du programme
        self.background = None
        self.background = arcade.load_texture("images/Terre.jpg")

        # Définition de l'espace physique pymunk et ses caractéristiques
        self.space = pymunk.Space()
        self.space.iterations = 35
        self.space.gravity = (0.0, -900.0)

        # Liste des sprites et lignes
        self.sprite_list = arcade.SpriteList()
        self.static_lines = []

        # Pour déplacer les objets
        self.shape_being_dragged = None
        self.last_mouse_position = 0, 0

        self.draw_time = 0
        self.processing_time = 0

        # Définition de l'objet et de l'astre définis de base
        self.physics = "Terre"
        self.mode = "Faire apparaitre une boîte"

        # Creation du sol
        floor_height = 100
        body = pymunk.Body(body_type=pymunk.Body.STATIC)
        shape = pymunk.Segment(body, [0, floor_height], [LargeurEcran, floor_height], 0.0)
        shape.friction = 1.0
        shape.elasticity = 0.5
        self.space.add(shape)
        self.static_lines.append(shape)

    def on_draw(self):
        #Affichage de l'écran
        arcade.start_render()

        DrawStartTime = timeit.default_timer()

        arcade.draw_texture_rectangle(LargeurEcran // 2, HauteurEcran // 2,
                                      LargeurEcran, HauteurEcran, self.background)

        self.sprite_list.draw() #Affichage des sprites

        self.draw_time = timeit.default_timer() - DrawStartTime

        # Affichage des consignes d'utilisation à l'écran
        output = f"Mode: {self.mode}"
        arcade.draw_text(output, 20, HauteurEcran - 20, arcade.color.WHITE)

        output = f"Localisation: {self.physics}"
        arcade.draw_text(output, 20, HauteurEcran - 40, arcade.color.WHITE)

        output = f"Touches utiles :"
        arcade.draw_text(output, 1770, HauteurEcran - 20, arcade.color.WHITE)

        output = f"ESPACE - Déplacer les objets"
        arcade.draw_text(output, 1688, HauteurEcran - 40, arcade.color.WHITE)

        output = f"Clic gauche - Faire apparaitre un objet"
        arcade.draw_text(output, 1631, HauteurEcran - 60, arcade.color.WHITE)

        output = f"B - Faire apparaitre une boîte"
        arcade.draw_text(output, 1684, HauteurEcran - 80, arcade.color.WHITE)

        output = f"H - Faire apparaitre un homme"
        arcade.draw_text(output, 1673, HauteurEcran - 100, arcade.color.WHITE)

        output = f"V - Faire apparaitre une voiture"
        arcade.draw_text(output, 1670, HauteurEcran - 120, arcade.color.WHITE)

        output = f"E - Faire apparaitre un éléphant"
        arcade.draw_text(output, 1669, HauteurEcran - 140, arcade.color.WHITE)

        output = f"S - Aller dans l'espace"
        arcade.draw_text(output, 1730, HauteurEcran - 160, arcade.color.WHITE)

        output = f"L - Aller sur la Lune"
        arcade.draw_text(output, 1745, HauteurEcran - 180, arcade.color.WHITE)

        output = f"M - Aller sur Mars"
        arcade.draw_text(output, 1751, HauteurEcran - 200, arcade.color.WHITE)

        output = f"T - Revenir sur Terre"
        arcade.draw_text(output, 1736, HauteurEcran - 220, arcade.color.WHITE)

        output = f"Pour faire disparaître les objets :"
        arcade.draw_text(output, 20, HauteurEcran - 80, arcade.color.WHITE)

        output = f"Les déplacer hors de l'écran"
        arcade.draw_text(output, 20, HauteurEcran - 100, arcade.color.WHITE)

    # Définition des différents objets

    def SpawnBoîtes(self, x, y):
        taille = 50
        masse = 30
        moment = pymunk.moment_for_box(masse, (taille, taille))
        Objet = pymunk.Body(masse, moment)
        Objet.position = pymunk.Vec2d(x, y)
        Forme = pymunk.Poly.create_box(Objet, (taille, taille))
        Forme.elasticity = 0.5
        Forme.friction = 1.0
        self.space.add(Objet, Forme)

        sprite = Formes(Forme, "images/Boîte.png", width=taille, height=taille)
        self.sprite_list.append(sprite)

    def SpawnHumain(self, x, y):
        hauteur = 200
        largeur=100
        masse = 100
        moment = pymunk.moment_for_box(masse, (hauteur, largeur))
        Objet = pymunk.Body(masse, moment)
        Objet.position = pymunk.Vec2d(x, y)
        Forme = pymunk.Poly.create_box(Objet, (hauteur, largeur))
        Forme.elasticity = 0.4
        Forme.friction = 1.0
        self.space.add(Objet, Forme)

        sprite = Formes(Forme, "images/Homme.png", width=largeur, height=hauteur)
        self.sprite_list.append(sprite)

    def SpawnCar(self, x, y):
        hauteur = 100
        largeur=250
        masse = 1000
        moment = pymunk.moment_for_box(masse, (hauteur, largeur))
        Objet = pymunk.Body(masse, moment)
        Objet.position = pymunk.Vec2d(x, y)
        Forme = pymunk.Poly.create_box(Objet, (hauteur, largeur))
        Forme.elasticity = 0.3
        Forme.friction = 1.0
        self.space.add(Objet, Forme)

        sprite = Formes(Forme, "images/Voiture.png", width=largeur, height=hauteur)
        self.sprite_list.append(sprite)

    def SpawnElephant(self, x, y):
        hauteur=200
        largeur=400
        masse=5000
        moment = pymunk.moment_for_box(masse, (hauteur, largeur))
        Objet = pymunk.Body(masse, moment)
        Objet.position = pymunk.Vec2d(x, y)
        Forme = pymunk.Poly.create_box(Objet, (hauteur, largeur))
        Forme.elasticity = 0.3
        Forme.friction = 1.0
        self.space.add(Objet, Forme)

        sprite = Formes(Forme, "images/Elephant.png", width=largeur, height=hauteur)
        self.sprite_list.append(sprite)

    def get_shape(self, x, y):
        # Détecte le clic
        shape_list = self.space.point_query((x, y), 1, pymunk.ShapeFilter())

        # Garde en mémoire l'endroit du clic
        if len(shape_list) > 0:
            shape = shape_list[0]

        else:
            shape = None

        return shape

    # Lorsqu'on appuie sur la souris
    def on_mouse_press(self, x, y, button, modifiers):

        if button == 1 and self.mode == "Déplacer les objets":
            self.last_mouse_position = x, y
            self.shape_being_dragged = self.get_shape(x, y)

        elif button == 1 and self.mode == "Faire apparaitre une boîte":
            self.SpawnBoîtes(x, y)

        elif button == 1 and self.mode == "Faire apparaitre un humain":
            self.SpawnHumain(x, y)

        elif button == 1 and self.mode == "Faire apparaitre une voiture":
            self.SpawnCar(x, y)

        elif button == 1 and self.mode == "Faire apparaitre un éléphant":
            self.SpawnElephant(x, y)

    # Lorsqu'on relache le bouton
    def on_mouse_release(self, x, y, button, modifiers):
        if button == 1:
            # Lorsqu'on relache le clic, l'objet tombe
            self.shape_being_dragged = None

    # Durant le déplacement du curseur
    def on_mouse_motion(self, x, y, dx, dy):
        if self.shape_being_dragged is not None:
            # Si on tient l'objet, il bouge avec le curseur
            self.last_mouse_position = x, y
            self.shape_being_dragged.shape.body.position = self.last_mouse_position
            self.shape_being_dragged.shape.body.velocity = dx * 20, dy * 20

    # Lorsqu'on appuie sur un bouton du clavier
    def on_key_press(self, symbol: int, modifiers: int):
        if symbol == arcade.key.SPACE:
            self.mode = "Déplacer les objets"

        elif symbol == arcade.key.B:
            self.mode = "Faire apparaitre une boîte"

        elif symbol == arcade.key.H:
            self.mode = "Faire apparaitre un humain"

        elif symbol == arcade.key.V:
            self.mode = "Faire apparaitre une voiture"

        elif symbol == arcade.key.E:
            self.mode = "Faire apparaitre un éléphant"

        elif symbol == arcade.key.S:
            self.space.gravity = (0.0, 0.0)
            self.space.damping = 1
            self.physics = "Espace"
            self.background = arcade.load_texture("images/Espace.png")

        elif symbol == arcade.key.T:
            self.space.damping = 1
            self.space.gravity = (0.0, -900.0)
            self.physics = "Terre"
            self.background = arcade.load_texture("images/Terre.jpg")

        elif symbol == arcade.key.M:
            self.space.damping = 0.9
            self.space.gravity = (0.0, -340.0)
            self.physics = "Mars"
            self.background = arcade.load_texture("images/Mars.jpg")

        elif symbol == arcade.key.L:
            self.space.damping = 0.9
            self.space.gravity = (0.0, -148.0)
            self.physics = "Lune"
            self.background = arcade.load_texture("images/Lune.jpg")

    def update(self, delta_time):
        start_time = timeit.default_timer()

        # Pour les objets qui sortent de l'écran
        for sprite in self.sprite_list:
            if sprite.pymunk_shape.body.position.y < 0:
                # On les supprimes de l'espace physique et de la liste physique
                self.space.remove(sprite.pymunk_shape, sprite.pymunk_shape.body)
                sprite.kill()

        # Mise à jour
        self.space.step(1 / 80.0)

        # Pour être sur que l'objet suive le curseur et que la gravité agisse sur cet objet
        if self.shape_being_dragged is not None:
            self.shape_being_dragged.shape.body.position = self.last_mouse_position
            self.shape_being_dragged.shape.body.velocity = 0, 0

        # Mise à jour : les images doivent suivre la position de l'objet physique
        for sprite in self.sprite_list:
            sprite.center_x = sprite.pymunk_shape.body.position.x
            sprite.center_y = sprite.pymunk_shape.body.position.y
            sprite.angle = math.degrees(sprite.pymunk_shape.body.angle)

        # Enregistrement du temps mis pour faire les actions, pour les afficher
        self.processing_time = timeit.default_timer() - start_time

window = Simulation(LargeurEcran, HauteurEcran, Titre)

arcade.run()


from django.db import models
from django.contrib.auth.models import User
from PIL import Image
from django.utils import timezone

# Create your models here.

class UserProfile(models.Model):

    owner = models.OneToOneField(User, on_delete=models.CASCADE, verbose_name='用户')
    nike_name = models.CharField('昵称', max_length=23, blank=True, default='')
    desc = models.TextField('个人简介', max_length=100, blank=True, default='', null=True)
    sign = models.CharField('个性签名', max_length=25, blank=True, default='', null=True)
    birthday = models.DateField('生日', null=True, blank=True)
    address = models.CharField('地址', max_length=100, blank=True, default='')
    title = models.CharField('头衔', max_length=100, default='善良的网友',blank=True)
    img = models.ImageField('头像', upload_to='profile_images',  default='/default/txdefault.jpg',blank=True)
    back_img = models.ImageField('个人主页背景', upload_to='user_page_backimg',  default='/default/backimg.jpg', blank=True, null=True)
    title_level = models.IntegerField('头衔类别,1站主,2管理员,3特殊,4普通', default=4)
    notice = models.TextField('发布公告', max_length=1000, null=True, blank=True, default='')
    def __str__(self):
        return self.nike_name

class Post(models.Model):
    id = models.AutoField(primary_key=True)
    post_owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="文章作者")
    owner_profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
    post_content = models.CharField('帖子内容', max_length=10000, blank=True)
    post_img1 = models.ImageField('帖子图片1', upload_to='post_images', blank=True, null=True)
    show_view = models.BooleanField('预览图是否模糊', default=False)
    pinned = models.BooleanField('是否顶置',default = False)

class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    user = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
    text = models.CharField('评论内容', max_length=500, blank=True, null=True)
    #like = models.IntegerField('点赞')

class Like(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, verbose_name='赞所属文章', blank=True, null=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='点赞者', blank=True, null=True)

class Notice(models.Model):
    id = models.AutoField(primary_key=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="公告拥有者")#这一条仅用于admin界面能够集成Notice便于管理
    creater = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name="公告发布者")
    notice = models.TextField('公告内容', max_length=300, blank=True)


# -*- coding: utf-8 -*-
""" MLSB Summer Events. """
from flask import render_template, send_from_directory, Response
from api import app, PICTURES, DB
from api.model import LeagueEvent, LeagueEventDate
from api.routes import Routes
from api.cached_items import get_website_base_data as base_data
from api.authentication import \
    get_user_information, api_require_login, are_logged_in, get_player_id
from api.advanced.league_event import get_year_events
import os.path
import json
NOT_FOUND = "sorry.jpg"
EVENT_FOLDER = 'events'

@app.route(Routes['eventspage'] + "/<int:year>/image/<int:league_event_id>")
def mlsb_event_image(year, league_event_id):
    # two potential file paths
    filepath = os.path.join(PICTURES, EVENT_FOLDER)
    year_filepath = os.path.join(filepath, str(year))

    # does the event even exist
    event = LeagueEvent.query.get(league_event_id)
    if event is None:
        return send_from_directory(filepath, NOT_FOUND)

    filename = event.name.lower().replace(" ", "_").strip() + ".png"

    # see if this has a particular image
    if os.path.isfile(os.path.join(year_filepath, filename)):
        return send_from_directory(year_filepath, filename)

    # send the general event image
    if os.path.isfile(os.path.join(filepath, filename)):
        return send_from_directory(filepath, filename)

    # no image can be found for the given event
    return send_from_directory(filepath, NOT_FOUND)

@app.route(Routes['eventspage'] + "/<int:year>" + "/json")
def events_page_json(year):
    return json.dumps(get_year_events())

@app.route(
    Routes['eventspage'] + "/signup/<int:league_event_date_id>",
    methods=["POST"]
)
@api_require_login
def signup_event(league_event_date_id):
    """Signup player for given event date."""
    league_event_date = LeagueEventDate.query.get(league_event_date_id)
    if league_event_date is None:
        return Response(
            json.dumps(False),
            status=404,
            mimetype="application/json"
        )
    player_id = get_player_id()
    success = league_event_date.signup_player(player_id)
    DB.session.commit()
    return Response(
        json.dumps(success),
        status=200,
        mimetype="application/json"
    )

@app.route(Routes['eventspage'] + "/<int:year>")
def events_page(year):
    """The events page for a given year"""
    events = get_year_events(year)
    if are_logged_in():
        for i in range(0, len(events)):
            event = LeagueEventDate.query.get(
                events[i]['league_event_date_id']
            )
            if event is None:
                events[i]['registered'] = False
            else:
                events[i]['registered'] = event.is_player_signed_up(
                    get_player_id()
                )
    return render_template("website/events.html",
                           dates=events,
                           route=Routes,
                           base=base_data(year),
                           title="Events",
                           year=year,
                           user_info=get_user_information())


from typing import Annotated
from fastapi import routing, File, UploadFile, status

router = routing.APIRouter(tags=["file"])

@router.post("/", status_code=status.HTTP_201_CREATED)
async def create_file(file: Annotated[bytes, File()]) -> dict:
    return {"file_size": len(file)}

@router.post("/upload", status_code=status.HTTP_201_CREATED, description="Upload a file")
async def create_upload_file(file: UploadFile) -> dict:
    return {"filename": file.filename, "content_type": file.content_type}


# Databricks notebook source
# MAGIC %md # Final Model
# MAGIC Prophet is a model from Facebook for time series forecasting. It combines piecewise linear(ish) functions with fourier series for cyclical properties and categorical encodings of holidays and events. See more here https://facebook.github.io/prophet/docs/quick_start.html#python-api\

# COMMAND ----------

# MAGIC %pip install prophet

# COMMAND ----------

import math
from datetime import datetime
from datetime import timedelta
from pyspark.sql import functions as f
from pyspark.sql.types import *
import pandas as pd
from prophet import Prophet
from prophet.plot import add_changepoints_to_plot
from prophet.utilities import regressor_coefficients
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_percentage_error
from matplotlib import pyplot as plt
from prophet.serialize import model_to_json, model_from_json

# COMMAND ----------

# MAGIC %md ## Prepare Data

# COMMAND ----------

# https://en.wikipedia.org/wiki/COVID-19_pandemic_in_Switzerland
COVID_START = datetime(2020, 3, 1).date()
# https://www.admin.ch/gov/en/start/documentation/media-releases.msg-id-84127.html
# date taken as the second easing of restrictions during 2021
COVID_END = datetime(2021, 6, 1).date()

MODEL_PATH = "/ConsumptionModel/Models/"
TRAIN_DATA_PATH = "/ConsumptionModel/Data/final_train.snappy.parquet"

MODEL_NAME = "totalconsumption_rolling7day.json"
CONSUMPTION_EXPR = "NE5Consumption + NE7Consumption" # i.e. "NE5Consumption + NE7Consumption", "NE5Consumption", "NE7Consumption"
ROLLING_WINDOW = 7
CONFIDENCE_INTERVAL = 0.95
USE_WEEK_SEASONALITY = False
USE_HOLIDAYS = False

# COMMAND ----------

def loadParquetToPandas(path):
  return (spark.read.format("parquet").load(path)
                                      .withColumn("y", f.expr(CONSUMPTION_EXPR))
                                      .withColumn("CosYearTemp", f.expr("Temperature * cos(dayofyear(Date) * 2 * pi() / 365)"))
                                      .withColumn("SinYearTemp", f.expr("Temperature * sin(dayofyear(Date) * 2 * pi() / 365)"))
                                      .selectExpr("Date as ds", "y", "CosYearTemp", "SinYearTemp")
                                      .orderBy("ds")
         ).toPandas().rolling(ROLLING_WINDOW, on="ds").mean().dropna()[:-ROLLING_WINDOW]

train = loadParquetToPandas(TRAIN_DATA_PATH)

# COMMAND ----------

# MAGIC %md ## Build Model

# COMMAND ----------

# add covid as a one-off holiday
covid = pd.DataFrame([
    {'holiday': 'covid', 'ds': COVID_START, 'lower_window': 0, 'ds_upper': COVID_END}
])

covid['upper_window'] = (covid['ds_upper'] - covid['ds']).dt.days

# COMMAND ----------

m = Prophet(holidays = covid, weekly_seasonality = USE_WEEK_SEASONALITY, changepoint_prior_scale = 0.005, changepoint_range=1, interval_width=CONFIDENCE_INTERVAL, uncertainty_samples = 10000)

if USE_HOLIDAYS:
  m.add_country_holidays(country_name='CH')

m.add_regressor("CosYearTemp", prior_scale = 0.05, standardize = True, mode = "additive")
m.add_regressor("SinYearTemp", prior_scale = 0.05, standardize = True, mode = "additive")
m.fit(train)

# COMMAND ----------

forecastTrain = m.predict(train)

# COMMAND ----------

fig = m.plot_components(forecastTrain)

# COMMAND ----------

fig = m.plot(forecastTrain)
a = add_changepoints_to_plot(fig.gca(), m, forecastTrain)

# COMMAND ----------

# MAGIC %md ## Error Metrics Train

# COMMAND ----------

mse = mean_squared_error(train["y"].to_numpy(), forecastTrain["yhat"].to_numpy())
mse

# COMMAND ----------

rmse = math.sqrt(mse)
rmse

# COMMAND ----------

mean_absolute_percentage_error(train["y"].to_numpy(), forecastTrain["yhat"].to_numpy())

# COMMAND ----------

regressor_coefficients(m)

# COMMAND ----------

# MAGIC %md ## Plots Train

# COMMAND ----------

plotData = pd.DataFrame(
  {
    "Actual": train["y"].array,
    "PredictedMean": forecastTrain["yhat"].array,
    "PredictedHigh": forecastTrain["yhat_upper"].array,
    "PredictedLow": forecastTrain["yhat_lower"].array
  },
  index=train["ds"]
)

plotData.index = pd.to_datetime(plotData.index)
plotData = plotData.sort_index()

# COMMAND ----------

def plot(data):
  fig, ax = plt.subplots()
  fig.set_size_inches(12, 8)
  fig.patch.set_facecolor('white')
  ax.plot(data.index, data["Actual"])
  ax.fill_between(data.index, data["PredictedHigh"], data["PredictedLow"], color='orange', alpha=.2)

# COMMAND ----------

plot(plotData)

# COMMAND ----------

plot(plotData[(plotData.index >= datetime(2019, 1, 1)) & (plotData.index < datetime(2019, 3, 1))])

# COMMAND ----------

# MAGIC %md ## Save Model

# COMMAND ----------

with open(MODEL_PATH + MODEL_NAME, 'w') as fout:
    fout.write(model_to_json(m))


from models.groundwater_model import groundwater_exponential
from data.importInput import prec, h_d, h_p, measurements, constants

import numpy as np
from numpy.random import normal

def EnKF_state(n=200):
    Sigma_m = constants["Sigma_m"]
    Sigma_y = constants["Sigma_y"]

    h_init = [normal(measurements[0], Sigma_y) for i in range(n)]
    h_init = np.array([min(h, constants["sl"]) for h in h_init])
    h_pred = h_init.copy()
    h_opt = h_init.copy()
    noisy_measurements, h_predictions, h_optimums = (
        [h_init.copy()],
        [h_pred.copy()],
        [h_opt.copy()],
    )
    for j, measurement in enumerate(measurements[1:]):
        # Predictions for all previous optima
        for i, optima in enumerate(h_opt):
            h_pred[i], _, _, _ = groundwater_exponential(
                optima, prec[j], h_d[j], h_p[j], constants
            )
            h_pred[i] += normal(0, Sigma_m)
            h_pred[i] = min(h_pred[i], constants["sl"])
        # Usually translation of model to measured variables

        # Define Covariance Matrices
        pred_mean = h_pred.mean()
        Sigma_x = 1 / (n - 1) * sum((h_pred - pred_mean) ** 2)
        Sigma_xy = Sigma_x

        # Add noise to Measurement
        meas_t = [measurement + normal(0, Sigma_y) for m in range(n)]

        # Fuse Prediction with Measurement
        K = Sigma_xy * ((Sigma_y + Sigma_x) ** (-1))
        for j, pred in enumerate(h_pred):
            h_opt[j] = pred + K * (meas_t[j] - pred)

        noisy_measurements.append(meas_t)
        h_predictions.append(h_pred.copy()), h_optimums.append(h_opt.copy())
    return h_predictions, h_optimums, noisy_measurements


#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import click
import logging
from pathlib import Path
from dotenv import find_dotenv, load_dotenv
import numpy as np
import pandas as pd
from tqdm import tqdm
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

from src.common import ensure_dir
from src.visualization.visualize import plot_process

@click.command()
@click.option('--input-filepath', type=click.Path(exists=True), default='data/interim/train_test.hdf5')
@click.option('--output-filepath', type=click.Path(), default='/tmp/a.png')
@click.option("--process-id", type=int)
@click.option("--object-id", type=int)
@click.option("--dpi", type=int, default=150)
def main(input_filepath, output_filepath, process_id, object_id, dpi):
    logger = logging.getLogger(__name__)
    logger.info('loading data from %s', input_filepath)

    train_test_events = pd.read_hdf(input_filepath, "train_test_events")

    if process_id is not None:
        logging.info("plotting process_id=%d", process_id)
        g = train_test_events[train_test_events.process_id==process_id]
        object_id = g.iloc[0].object_id
        fig, _ = plot_process(g)
        fn = output_filepath
        ensure_dir(fn)
        fig.savefig(fn, dpi=dpi, bbox_inches='tight')
        # plt.close()
    elif object_id is not None:
        grp = train_test_events[train_test_events.object_id==object_id].groupby('process_id')
        for process_id, g in grp:
            logging.info("plotting process_id=%d", process_id)
            fig, _ = plot_process(g)
            fn = os.path.join(output_filepath, f"plt_obj{object_id}_pr{process_id}.png")
            ensure_dir(fn)
            fig.savefig(fn, dpi=dpi, bbox_inches='tight')
            plt.close()
    else:
        g = [(process_id, train_test_events[train_test_events.process_id==process_id])]
        fig, _ = plot_process(g)
        fig.savefig(output_filepath, dpi=dpi, bbox_inches='tight')

if __name__ == '__main__':
    log_fmt = '%(asctime)s - %(levelname)s - %(message)s'
    logging.basicConfig(level=logging.INFO, format=log_fmt)

    # not used in this stub but often useful for finding various files
    project_dir = Path(__file__).resolve().parents[2]

    # find .env automagically by walking up directories until it's found, then
    # load up the .env entries as environment variables
    load_dotenv(find_dotenv())

    main()


# -*- coding: utf-8 -*-
"""
Created on Thu Oct  6 13:39:20 2022

@author: zfair
"""

import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
from datetime import datetime
from pyproj import Proj, transform
from scipy.interpolate import RectBivariateSpline
import ulmo

#---------------#
def process_raw_data(df):
    """
    Takes unprocessed site data and makes it more easily readable for further
    analysis. Currently works for the magnaprobe data gathered from Alaska 2022.

    Parameters
    ----------
    txt_file : str
        .txt file containing lat/lon coordinates and snow depth data from one of
        the SnowEx Alaska sites.

    Returns
    -------
    df : DataFrame
        DataFrame containing processed magnaprobe data, including snow depth (in meters),
        condensed lat/lon coordinates, and easting/northing estimates.

    """

    #df = pd.read_csv(txt_file, header=1)

    # Include only rows with data
    df = df.iloc[2:]

    # Convert numeric data to floats
    df['DepthCm'] = pd.to_numeric(df['DepthCm'])
    df['altitudeB'] = pd.to_numeric(df['altitudeB'])
    df[['latitude_a', 'LatitudeDDDDD']] = df[['latitude_a', 'LatitudeDDDDD']].apply(pd.to_numeric)
    df[['Longitude_a', 'LongitudeDDDDD']] = df[['Longitude_a', 'LongitudeDDDDD']].apply(pd.to_numeric)

    # Combine coordinates into single columns
    df['Latitude'] = df['latitude_a'] + df['LatitudeDDDDD']
    df['Longitude'] = df['Longitude_a'] + df['LongitudeDDDDD']

    # Convert snow depth to meters
    df['Depth'] = df['DepthCm'] / 100.

    # Estimate easting/northing coordinates
    inp = Proj(init='epsg:4326')
    outp = Proj(init='epsg:32606')

    df['Easting'], df['Northing'] = transform(inp, outp,
                                              df['Longitude'], df['Latitude'])

    # Rename time and elevation columns for consistency with other scripts
    df = df.rename(columns={'TIMESTAMP': 'UTCdoy',
                            'altitudeB': 'Elevation'})

    # Plotting, for debugging/quick views
    if True:
        plt.scatter(df['Easting'], df['Northing'], c=df['Depth'])
        plt.xlabel('easting [m]')
        plt.ylabel('northing [m]')
        plt.show()

    return df

#---------------#
def snotel_fetch(sitecode, variablecode='SNOTEL:SNWD_D', start_date='1950-10-01', end_date='2020-12-31'):
    """
    Accesses SNOTEL data from the cloud using the CUAHSI HydroPortal.

    Parameters
    ----------
    sitecode: str
        Numeric ID for SNOTEL site, in string format.
    variablecode: str
        Dataset to be accessed.
        Default: SNOTEL snow depth.
    start_date: str
        Start date of SNOTEL time series, in YYYY-MM-DD format.
        Default: 1950-10-01
    end_date: str
        End date of SNOTEL time series, in YYYY-MM-DD format.
        Default: 2020-12-31

    Returns
    -------
    values_df: DataFrame
        DataFrame containing desired SNOTEL dataset.

    """

    #print(sitecode, variablecode, start_date, end_date)
    values_df = None
    wsdlurl = 'https://hydroportal.cuahsi.org/Snotel/cuahsi_1_1.asmx?WSDL'
    try:
        #Request data from the server
        site_values = ulmo.cuahsi.wof.get_values(wsdlurl, sitecode, variablecode, start=start_date, end=end_date)
        #Convert to a Pandas DataFrame
        values_df = pd.DataFrame.from_dict(site_values['values'])
        #Parse the datetime values to Pandas Timestamp objects
        values_df['datetime'] = pd.to_datetime(values_df['datetime'], utc=True)
        #Set the DataFrame index to the Timestamps
        values_df = values_df.set_index('datetime')
        #Convert values to float and replace -9999 nodata values with NaN
        values_df['value'] = pd.to_numeric(values_df['value']).replace(-9999, np.nan)
        #Remove any records flagged with lower quality
        values_df = values_df[values_df['quality_control_level_code'] == '1']
    except:
        print("Unable to fetch %s" % variablecode)

    return values_df
#---------------#
def add_dowy(df, col=None):
    """
    Companion function to snotel_fetch. Converts days of year to days of water year.

    Parameters
    ----------
    df: Dataframe
        DataFrame that contains SNOTEL data (values_df in snotel_fetch).
    col: Pandas column
        DataFrame column that contains day-of-year information. If passed as None, then attempts to access day of year from the indeces.
        Default: None

    Returns
    -------
    None

    """

    if col is None:
        df['doy'] = df.index.dayofyear
    else:
        df['doy'] = df[col].dayofyear
    # Sept 30 is doy 273
    df['dowy'] = df['doy'] - 273
    df.loc[df['dowy'] <= 0, 'dowy'] += 365
#---------------#
def coregister_nlcd_data(is2_pd, land_cover_tif, txt_name):
    """
    Uses the nearest-neighbor approach to coregister National Land Cover Data (NLCD) with ICESat-2 data. The nearest-neighbor approach is needed to preserve land cover values that would be lost with spline interpolation.

    Unlike the lidar coregistration, matched land cover data is saved to a txt file to speed up the process for subsequent code runs.

    Parameters
    ----------
    is2_pd: DataFrame
        DataFrame containing ICESat-2 data, processed using the lidar_processing scripts.
    land_cover_tif: Xarray tiff
        Xarray object containing land cover values across Alaska.
    txt_name: str
        Name of text file that will store coregistered land cover information.

    Returns
    -------
    None

    """
    # Initialize the text file
    nlcd_file = open(txt_name, 'w')

    # Use nearest-neighbor approach to match land cover with ATL03 segments
    for i in np.arange(len(is2_pd)):
        x = is2_pd['x'][i]
        y = is2_pd['y'][i]

        dx = x - land_cover_tif.x
        dy = y - land_cover_tif.y
        d = np.sqrt(dx**2 + dy**2)

        x_idx = d.argmin(dim='x')[0].values
        y_idx = d.argmin(dim='y')[0].values

        tmp = land_cover_tif[:, x_idx, y_idx].values

        # Write land cover data to text file iteratively
        nlcd_file.write('%f\n' %tmp)

    # Close text file
    nlcd_file.close()
#---------------#
def process_nlcd_data(is2_pd, txt_name):
    """
    Takes coregistered land cover values and translates them to more descriptive labels. Also corrects misclassified values that were found over the ACP site in Alaska.

    Parameters
    ----------
    is2_pd: DataFrame
        DataFrame containing ICESat-2 data, processed using the lidar_processing scripts.

    txt_name: str
        Name of text file that contains coregistered land cover information.

    Returns
    -------
    is2_pd: DataFrame
        An updated ICESt-2 dataframe that contains both land cover values and the corresponding labels.

    """

    # Add land cover data to DataFrame
    is2_pd['land_cover_value'] = pd.read_table(txt_name, header=None).values

    path = '/home/jovyan/icesat2-snowex/jsons-shps/land-cover-maps/'
    # Translate values into land cover classification
    nlcd_legend = pd.read_csv(f'{path}/NLCD_landcover_legend_2018_12_17_39kiznbMV7t0juCIU61D.csv', header=0)

    is2_pd['land_cover'] = ''
    # Apply legend monikers to new column. Also converts strings to something that is easier to type.
    for i,value in enumerate(is2_pd['land_cover_value']):
        # Account for misclassified values
        if (value==51.0) | (value==52.0):
            is2_pd['land_cover'][i] = 'Shrub'
        elif (value == 72.0) | (value==71.0):
            is2_pd['land_cover'][i] = 'Herbaceous'
        else:
            is2_pd['land_cover'][i] = nlcd_legend['Legend'].loc[value]

    return is2_pd
#---------------#
def coregister_gfcc_data(is2_pd, forest_cover_tif):
    """
    Co-registers ICESat-2 segments with GFCC forest cover data.

    Parameters
    ----------
    is2_pd: DataFrame
        DataFrame containing ICESat-2 data, processed using the lidar_processing scripts.
    forest_cover_tif: Xarray TIFF
        TIFF raster containing forest cover percentages.

    Returns
    -------
    is2_gfcc_pd: DataFrame
        An updated ICESat-2 dataframe that contains forest cover data in addition to other variables.

    """

    # Raster coordinates
    x0, y0 = np.array(forest_cover_tif.x), np.array(forest_cover_tif.y)

    # Generate spline for land cover data
    dem_cover = np.array(forest_cover_tif.sel(band=1))[::-1,:]
    interpolator = RectBivariateSpline(np.array(y0)[::-1],
                                       np.array(x0),
                                       dem_cover)

    # Use the constructed spline to align GFCC with ICESat-2
    gfcc_pd = pd.DataFrame()
    for beam in np.unique(is2_pd['beam']):
        is2_tmp = is2_pd.loc[is2_pd['beam']==beam]

        # ICESat-2 x/y coordinates
        xn, yn = is2_tmp['x'].values, is2_tmp['y'].values

        # Define indices within x/y bounds
        i1 = (xn>np.min(x0)) & (xn<np.max(x0))
        i1 &= (yn>np.min(y0)) & (yn<np.max(y0))

        # Co-register forest cover with ICESat-2, using x/y coordinates and spline
        forcov = interpolator(yn[i1], xn[i1], grid=False)
        tmp = pd.DataFrame(data={'forest_cover': forcov})
        gfcc_pd = pd.concat([gfcc_pd, tmp])

    # Add forest cover data to ICESat-2 dataframe
    is2_gfcc_pd = is2_pd
    is2_gfcc_pd['forest_cover'] = gfcc_pd['forest_cover'].values

    return is2_gfcc_pd


import functools

from flask import (
    Blueprint,
    flash,
    g,
    jsonify,
    redirect,
    render_template,
    request,
    session,
    url_for,
    request,
)
from werkzeug.security import check_password_hash, generate_password_hash

dressbp = Blueprint("dress", __name__, url_prefix="/v1/")

@dressbp.route("/dresses", methods=["GET"])
def get_dresses():
    dresses = {
        "dress_list": [
            {"type": "suit", "color": "black"},
            {"type": "one-piece", "color": "yellow"},
        ]
    }

    x = jsonify(dresses)
    return x

@dressbp.route("/dresses", methods=["POST"])
def post_dresses():
    req = request.json

    x = jsonify({"status": 200})
    return x


import os
import random
import torch
import numpy as np
from collections import defaultdict
from torch.optim import Optimizer
# from Config import CFG
from sklearn.metrics import accuracy_score
# cfg = CFG()

# def scheduler(epoch):
#         #return start_lr
#         # num_epoch = cfg.epochs / 2
#         num_epoch = cfg.decay_epochs
#         start_lr = cfg.start_lr
#         min_lr = cfg.min_lr

#         lr = (num_epoch-epoch)/num_epoch * (start_lr-min_lr) + min_lr
#         lr = max(min_lr,lr)
#         return lr

def adjust_learning_rate(optimizer, lr):
    for param_group in optimizer.param_groups:
        param_group['lr'] = lr

def get_learning_rate(optimizer):
    return optimizer.param_groups[0]['lr']
    # return optimizer.param_groups[0]['lr']

def time_to_str(t, mode='min'):
    if mode == 'min':
        t  = int(t) / 60
        hr = t // 60
        min = t % 60
        return '%2d hr %02d min' % (hr, min)

    elif mode=='sec':
        t   = int(t)
        min = t // 60
        sec = t % 60
        return '%2d min %02d sec' % (min, sec)

    else:
        raise NotImplementedError

""" Seeding the randomness. """
def seeding(seed):
    random.seed(seed)
    os.environ["PYTHONHASHSEED"] = str(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.backends.cudnn.deterministic = True

def set_all_random_seed(seed):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    return seed

""" Metrics ------------------------------------------ """
def precision(y_true, y_pred):
    intersection = (y_true * y_pred).sum()
    return (intersection + 1e-15) / (y_pred.sum() + 1e-15)

def recall(y_true, y_pred):
    intersection = (y_true * y_pred).sum()
    return (intersection + 1e-15) / (y_true.sum() + 1e-15)

def F2(y_true, y_pred, beta=2):
    p = precision(y_true,y_pred)
    r = recall(y_true, y_pred)
    return (1+beta**2.) *(p*r) / float(beta**2*p + r + 1e-15)

# def dice_score(y_true, y_pred):
#     return (2 * (y_true * y_pred).sum() + 1e-15) / (y_true.sum() + y_pred.sum() + 1e-15)

# def jac_score(y_true, y_pred):
#     intersection = (y_true * y_pred).sum()
#     union = y_true.sum() + y_pred.sum() - intersection
#     return (intersection + 1e-15) / (union + 1e-15)

def calculate_metrics(y_true, y_pred):
    # y_true = y_true.detach().cpu().numpy()
    # y_pred = y_pred.detach().cpu().numpy()

    y_pred = y_pred > 0.5
    y_pred = y_pred.reshape(-1)
    y_pred = y_pred.astype(np.float32)

    y_true = y_true > 0.5
    y_true = y_true.reshape(-1)
    y_true = y_true.astype(np.float32)

    ## Score
    score_recall = recall(y_true, y_pred)
    score_precision = precision(y_true, y_pred)
    score_fbeta = F2(y_true, y_pred)
    score_acc = accuracy_score(y_true, y_pred)

    return [score_recall, score_precision, score_acc, score_fbeta]

class dotdict(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

def Specifity(y_true, y_pred):
    specifity = []
    # threshold = np.linspace(0.45, 0.55, 100)
    threshold = np.linspace(0, 1, 100)
    # pdb.set_trace()
    for t in threshold:
        predict = (y_pred > t).astype(np.float32)

        tp = ((predict>=0.5) & (y_true>=0.5)).sum()
        tn = ((predict<0.5) & (y_true<0.5)).sum()
        fp = ((predict>=0.5) & (y_true< 0.5)).sum()
        fn = ((predict< 0.5) & (y_true>=0.5)).sum()

        # acc2 = (tp+tn)/(tp+tn+fp+fn+1e-15)
        # p = tp/(tp+fp+1e-15)
        # r = tp/(tp+fn+1e-15)
        spe = tn/(tn+fp+1e-15)

        # accuracy1.append(acc1)
        # accuracy2.append(acc2)
        # precision.append(p)
        # recall.append(r)
        specifity.append(spe)

    specifity=np.array(specifity)

    return specifity.mean()

def calculate_acc_pre_rec(y_true, y_pred):

    y_pred = y_pred > 0.5
    y_pred = y_pred.reshape(-1)
    y_pred = y_pred.astype(np.float32)

    y_true = y_true > 0.5
    y_true = y_true.reshape(-1)
    y_true = y_true.astype(np.float32)

    ## Score
    score_acc = accuracy_score(y_true, y_pred)
    score_precision = precision(y_true, y_pred)
    score_recall = recall(y_true, y_pred)

    return [score_acc, score_precision, score_recall]


from PIL import Image as _PIL_Image
import numpy as _numpy

class Hex:
    """Stores the values as an int and converts to a string for display"""
    def __init__(self, num):
        if isinstance(num, int):
            self.__val = num

    def __repr__(self):
        return str(self)

    def __str__(self):
        return "{:#04x}".format(self.__val)

    def __int__(self):
        return self.__val

    def __eq__(self, other):
        try:
            other = int(other)
            return other == int(self)
        except:
            return False

    @classmethod
    def from_str(cls, num, delimiter=" "):
        num = num.split(delimiter)

        if len(num) == 1:
            return Hex(int(num, 16))
        else:
            out = []
            for i in num:
                out.append(Hex(int(i, 16)))
            return out

    @classmethod
    def from_int(cls, num):
        return Hex(num)

    @classmethod
    def from_bytes(cls, num):
        if len(num) > 1:
            return [Hex(n) for n in num]
        else:
            return Hex(num[0])

    @classmethod
    def from_array(cls, nums):
        out = []
        for i, num in enumerate(nums):
            if isinstance(num, int):
                out.append(Hex(num))
            elif isinstance(num, str):
                result = Hex.from_str(num)
                if isinstance(result, list):
                    out.extend(result)
                else:
                    out.append(result)
            elif isinstance(num, bytes):
                out.append(Hex.from_bytes(num))
        return out

# ITF STANDS FOR "IMAGE TEXT FORMAT" ---> TESTING PURPOSES

# class Image:
#     elements = []
#
#     supported = ["itf", "png"]
#     def __init__(self, fileName):
#         ext = fileName.split(".")[-1]
#         if ext not in Image.supported:
#             raise TypeError("File format '{}' not supported".format(ext))
#         self.__file = open(fileName, "rb")
#         self.__read()
#
#     def __read(self):
#         EOF = int(self.__file.seek(0, 2))
#         __raw = []
#         self.__file.seek(0)
#         while self.__file.tell() < EOF:
#             chunk = self.__file.read(1000)
#             print(chunk)
#             chunk = Hex.from_bytes(chunk)
#             if isinstance(chunk, list):
#                 __raw.extend(chunk)
#             else:
#                 __raw.append(chunk)
#
#         if __raw[:8] != Hex.from_str("89 50 4e 47 0d 0a 1a 0a"):
#             print("FORMAT NOT SUPPORTED")
#             print(__raw[:8])
#             return -1
#
#         print(__raw)
#
#     def __del__(self):
#         self.__file.close()

class Image:
    def __init__(self, path):
        __img = _PIL_Image.open(path)
        # __img.show()
        print(__img.info)
        self.__height = __img.height
        self.__width = __img.width
        self.pixels = list(__img.getdata())

    def __del__(self):
        del self.pixels

    @property
    def height(self):
        return self.__height

    @property
    def width(self):
        return self.__width

i = Image("../../resources/BCobble.png")
print(i.pixels[0])
print(i.height, i.width)


from config import constants
from utils import logutils
from git.gitsearch import GitDataIngestion

logger = logutils.get_logger("Git Data Fetch")

def git_fetch_and_persist():
    git_search_agent = GitDataIngestion()
    for product, search_query_list in constants.PRODUCT_SEARCH_QUERY_DICT.items():
        for search_query in search_query_list:
            try:
                git_search_agent.data_ingestion_pipeline(search_query=search_query, product=product)
                logger.info("Successfully inserted data for product %s" % product)
            except Exception:
                logger.error("An error occurred while querying for %s " % search_query)


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('eatery_profile', '0014_auto_20150522_0955'),
    ]

    operations = [
        migrations.AlterField(
            model_name='location',
            name='eatery_profile',
            field=models.OneToOneField(to='eatery_profile.eatery_profile'),
        ),
    ]


# -*- coding: utf-8 -*-
#!/usr/bin/python

# TODO: DynamoDBに現在雨が降っているかの状態を登録し、メッセージの出し分けをする

import requests
from datetime import datetime, timedelta
import json
from pygooglechart import Chart
from pygooglechart import SimpleLineChart
from pygooglechart import Axis
import math
from pyshorteners import Shortener
import ConfigParser
from slackclient import SlackClient
import time
import boto3

inifile = ConfigParser.SafeConfigParser()
inifile.read("./config.ini")
GOOGLE_API_KEY = inifile.get('rainfall', 'google_api_key')
YAHOO_APP_ID = inifile.get('rainfall', 'yahoo_app_id')
LON = inifile.get('rainfall', 'lon')
LAT = inifile.get('rainfall', 'lat')
MAP_IMAGE_X = inifile.get('rainfall', 'map_image_x')
MAP_IMAGE_Y = inifile.get('rainfall', 'map_image_y')
ALERT_THRESH = float(inifile.get('rainfall', 'alert_thresh'))
ZOOM = inifile.get('rainfall', 'zoom')

def getTimeString(offset_minutes):
    d = datetime.now() + timedelta(hours=9, minutes=offset_minutes)
    return d.strftime("%H:%M")

def getRainfallRadarUrl(lat, lon, zoom, width, height):
    # http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/static.html#exp_weather
    url = "http://map.olp.yahooapis.jp/OpenLocalPlatform/V1/static?appid=" + YAHOO_APP_ID + "&lat=" + str(lat) + "&lon=" + str(lon) + "&z=" + str(zoom) + "&width=" + str(width) + "&height=" + str(height) + "&overlay=type:rainfall"
    return url

def shorten_url(url):
    shortener = Shortener('Google', api_key=GOOGLE_API_KEY)
    return shortener.short(url)

def lambda_handler(event, context):
    # http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html

    url = "http://weather.olp.yahooapis.jp/v1/place"

    payload = {'appid': YAHOO_APP_ID, 'coordinates': LON + "," + LAT, 'output': 'json'}
    result =  requests.get(url, params=payload)

    timeString = getTimeString(30)
    send_message = False

    json_data = json.loads(result.content)

    rainfall = json_data['Feature'][0]['Property']['WeatherList']['Weather']#[3]['Rainfall']

    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table(inifile.get('dynamodb', 'table_id'))
    key_id = int(inifile.get('dynamodb', 'id_value'))
    result = table.get_item(
        Key={
            'Id': key_id
        }
    )

    if 'raining' not in result:
        table.put_item(
            Item={
                "Id":key_id,
                "raining": False,
                "source": inifile.get('dynamodb', 'source')}
        )
    raining = result['Item']['raining']

    if rainfall[3]['Rainfall'] >= ALERT_THRESH and raining == False:
        send_message = True
        message = timeString + "で" + str(rainfall[3]['Rainfall']) + "mm/hの雨が予想されます。"
    elif rainfall[3]['Rainfall'] < ALERT_THRESH and raining == True:
        send_message = True
        message = timeString + "で" + "雨が止む予報です。"
    elif rainfall[3]['Rainfall'] >= ALERT_THRESH and raining == True:
        send_message = True
        message = timeString + "で" + str(rainfall[3]['Rainfall']) + "mm/hの雨が引き続き降る予報です。"
    elif rainfall[3]['Rainfall'] < ALERT_THRESH and raining == False:
        send_message = False

    if rainfall[3]['Rainfall'] >= ALERT_THRESH:
        table.put_item(
            Item={
                "Id":key_id,
                "raining": True,
                "source": inifile.get('dynamodb', 'source')}
        )
    else:
        table.put_item(
            Item={
                "Id":key_id,
                "raining": False,
                "source": inifile.get('dynamodb', 'source')}
        )

    if send_message == True:

        width = 500
        height = 150
        chart = SimpleLineChart(width, height)
        chart.set_colours(['719CFF'])

        rainfall_array = [rainfall[0]['Rainfall'], rainfall[1]['Rainfall'], rainfall[2]['Rainfall'], rainfall[3]['Rainfall'], rainfall[4]['Rainfall'], rainfall[5]['Rainfall'], rainfall[6]['Rainfall']]
        chart.add_data(rainfall_array)

        chart.set_axis_labels(Axis.BOTTOM, [getTimeString(0), getTimeString(10), getTimeString(20), getTimeString(30), getTimeString(40), getTimeString(50), getTimeString(60)])
        chart.set_legend(['Rainfall(mm/h)'])
        chart.set_legend_position('bv')

        if math.ceil(max(rainfall_array)) < 1.0:
            chart.y_range=(0, 1)
            chart.set_axis_range(Axis.LEFT, 0, 1)
        else:
            chart.y_range=(0, math.ceil(max(rainfall_array)))
            chart.set_axis_range(Axis.LEFT, 0, math.ceil(max(rainfall_array)))

        d = datetime.now() + timedelta(hours=9, minutes=30)
        date_str = d.strftime("|date:%Y%m%d%H%M|datelabel:on")

        radar_url = "http://weather.yahoo.co.jp/weather/zoomradar/?lat=" + LAT + "&lon=" + LON + "&z=12"
        rainfall_image_url = getRainfallRadarUrl(LAT, LON, ZOOM, MAP_IMAGE_X, MAP_IMAGE_Y) + date_str

        chart_url = shorten_url(chart.get_url())
        rainfall_image_url = shorten_url(rainfall_image_url)
        radar_url = shorten_url(radar_url)

        message = unicode(message, 'utf-8') + "\n" + chart_url + "\n" + timeString + u"の雨雲予想: " + rainfall_image_url + "\n" + u"詳細: " + radar_url + "\n"

        sc = SlackClient(inifile.get('slack', 'token'))
        res = sc.api_call("chat.postMessage", channel=inifile.get('slack', 'channel'), text=message, username=inifile.get('slack', 'username'), icon_emoji=inifile.get('slack', 'icon_emoji'))
        print(res)


import torch.nn as nn
import torch.nn.functional as F
import torch

def weights_init_normal(m):
    classname = m.__class__.__name__
    if classname.find("Conv") != -1:
        torch.nn.init.normal_(m.weight.data, 0.0, 0.02)
        if hasattr(m, "bias") and m.bias is not None:
            torch.nn.init.constant_(m.bias.data, 0.0)
    elif classname.find("BatchNorm2d") != -1:
        torch.nn.init.normal_(m.weight.data, 1.0, 0.02)
        torch.nn.init.constant_(m.bias.data, 0.0)

def initialize_weights(net):
    for m in net.modules():
        if isinstance(m, nn.Conv2d):
            m.weight.data.normal_(0, 0.02)
            m.bias.data.zero_()
        elif isinstance(m, nn.ConvTranspose2d):
            m.weight.data.normal_(0, 0.02)
            m.bias.data.zero_()
        elif isinstance(m, nn.Linear):
            m.weight.data.normal_(0, 0.02)
            m.bias.data.zero_()
        elif isinstance(m, nn.BatchNorm2d):
            m.weight.data.fill_(1)
            m.bias.data.zero_()

##############################
#           RESNET
##############################

class resnet_block(nn.Module):
    def __init__(self, channel, kernel, stride, padding):
        super(resnet_block, self).__init__()
        self.channel = channel
        self.kernel = kernel
        self.strdie = stride
        self.padding = padding
        self.conv1 = nn.Conv2d(channel, channel, kernel, stride, padding)
        self.conv1_norm = nn.InstanceNorm2d(channel)
        self.conv2 = nn.Conv2d(channel, channel, kernel, stride, padding)
        self.conv2_norm = nn.InstanceNorm2d(channel)

        initialize_weights(self)

    def forward(self, input):
        x = F.relu(self.conv1_norm(self.conv1(input)), True)
        x = self.conv2_norm(self.conv2(x))

        return input + x #Elementwise Sum

class GeneratorRes(nn.Module):
    # initializers
    def __init__(self, in_nc, out_nc, nf=64, nb=3):
        super(GeneratorRes, self).__init__()
        self.input_nc = in_nc
        self.output_nc = out_nc
        self.nf = nf
        self.nb = nb
        self.down_convs = nn.Sequential(
            nn.Conv2d(in_nc, nf, 7, 1, 3), #k7n64s1
            nn.InstanceNorm2d(nf),
            nn.ReLU(True),
            nn.Conv2d(nf, nf * 2, 3, 2, 1), #k3n128s2
            nn.Conv2d(nf * 2, nf * 2, 3, 1, 1), #k3n128s1
            nn.InstanceNorm2d(nf * 2),
            nn.ReLU(True),
            nn.Conv2d(nf * 2, nf * 4, 3, 2, 1), #k3n256s1
            nn.Conv2d(nf * 4, nf * 4, 3, 1, 1), #k3n256s1
            nn.InstanceNorm2d(nf * 4),
            nn.ReLU(True),
        )

        self.resnet_blocks = []
        for i in range(nb):
            self.resnet_blocks.append(resnet_block(nf * 4, 3, 1, 1))

        self.resnet_blocks = nn.Sequential(*self.resnet_blocks)

        self.up_convs = nn.Sequential(
            nn.ConvTranspose2d(nf * 4, nf * 2, 3, 2, 1, 1), #k3n128s1/2
            nn.Conv2d(nf * 2, nf * 2, 3, 1, 1), #k3n128s1
            nn.InstanceNorm2d(nf * 2),
            nn.ReLU(True),
            nn.ConvTranspose2d(nf * 2, nf, 3, 2, 1, 1), #k3n64s1/2
            nn.Conv2d(nf, nf, 3, 1, 1), #k3n64s1
            nn.InstanceNorm2d(nf),
            nn.ReLU(True),
            nn.Conv2d(nf, out_nc, 7, 1, 3), #k7n3s1
            nn.Tanh(),
        )

        initialize_weights(self)

    # forward method
    def forward(self, input):
        x = self.down_convs(input)
        x = self.resnet_blocks(x)
        output = self.up_convs(x)

        return output
##############################
#        Discriminator
##############################

class Discriminator(nn.Module):
    def __init__(self, in_channels=3):
        super(Discriminator, self).__init__()

        def discriminator_block(in_filters, out_filters, normalization=True):
            """Returns downsampling layers of each discriminator block"""
            layers = [nn.Conv2d(in_filters, out_filters, 4, stride=4)]
            if normalization:
                layers.append(nn.InstanceNorm2d(out_filters))
            layers.append(nn.LeakyReLU(0.2, inplace=True))
            return layers

        self.model = nn.Sequential(
            *discriminator_block(in_channels, 256),
            *discriminator_block(256, 512),
            nn.Conv2d(512, 1, 1,stride=1, bias=False),
            nn.Sigmoid()
        )

    def forward(self, img):
        # Concatenate image and condition image by channels to produce input
        return self.model(img)

 ################################
 #       VGG19
 ##################################

class VGG19(nn.Module):
    def __init__(self, init_weights=None, feature_mode=False, batch_norm=False, num_classes=1000):
        super(VGG19, self).__init__()
        self.cfg = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M']
        self.init_weights = init_weights
        self.feature_mode = feature_mode
        self.batch_norm = batch_norm
        self.num_clases = num_classes
        self.features = self.make_layers(self.cfg, batch_norm)
        self.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, num_classes),
        )
        if not init_weights == None:
            self.load_state_dict(torch.load(init_weights))

    def make_layers(self, cfg, batch_norm=False):
        layers = []
        in_channels = 3
        for v in cfg:
            if v == 'M':
                layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
            else:
                conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
                if batch_norm:
                    layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
                else:
                    layers += [conv2d, nn.ReLU(inplace=True)]
                in_channels = v
        return nn.Sequential(*layers)

    def forward(self, x):
        if self.feature_mode:
            module_list = list(self.features.modules())
            for l in module_list[1:27]:                 # conv4_4
                x = l(x)
        if not self.feature_mode:
            x = x.view(x.size(0), -1)
            x = self.classifier(x)

        return x


import sys
import ctypes
import win32com.shell.shell as shell
import webbrowser
import subprocess
import time
from multiprocessing import freeze_support

from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QVBoxLayout, QLabel, QTableView
from PyQt5.QtCore import pyqtSlot, QModelIndex, QAbstractTableModel, Qt

from DBManager import DBManager
from UIthread import *

########################################################################################################################
# UI 클래스
# UI 를 구성하기 위한 클래스
# UI 뿐만 아니라 각 UI 위젯 간의 시그널을 관리하기 위한 시그널 핸들러 함수도 정의하고 있다.
########################################################################################################################
class UI(QWidget):
    def __init__(self):
        super().__init__()
        self.db = DBManager()
        self.cached_file_list = [] # DB를 통해 검색하지 않고, 메모리에 올려서 검색을 수행하기 위한 파일 리스트
        self.count = 0 # 사용자 입력값이 2개 이상일 때 처리해주기 위해 구성

        # 테이블 갱신
        self.initUI()

        # 디렉토리를 탐색해서 파일 리스트를 얻은 후, DB에 insert 하는 작업까지 수행하는 쓰레드
        self.scan_thread = ScanThread()
        self.scan_thread.set_db(self.db)
        self.scan_thread.finish_scan_signal.connect(self.finish_scan)
        self.start_thread(mode = 1)

        # DB 정보를 읽기 위한 쓰레드
        self.read_db_thread = ReadDBThread()
        self.read_db_thread.set_db(self.db)
        self.read_db_thread.finish_read_signal.connect(self.finish_read)
        self.start_thread(mode = 0)

        # 새로 생성된 파일정보를 DB에 넣기 위한 쓰레드
        self.insert_db_thread = InsertDBThread()
        self.insert_db_thread.set_db(self.db)

        # 삭제된 파일정보를 DB에서 제거하기 위한 쓰레드
        self.delete_db_thread = DeleteDBThread()
        self.delete_db_thread.set_db(self.db)

        # 각 드라이브 별로 파일 변화 감지를 수행하는 쓰레드를 관리하는 쓰레드
        self.manager_observer_thread = ManagerObserverThread()
        self.manager_observer_thread.file_change_signal.connect(self.control_updated_file)
        self.start_thread(mode = 2)

        self.check_time = checkTime()
        self.check_time.timeout.connect(self.processing_time_out)

    ####################################################################################################################
    # initTable
    # = 테이블 표시를 위한 기본 설정을 하는 메소드이다.
    #   테이블의 entry를 클릭 했을 때, 파일이나 디렉토리를 실행하기 위한 이벤트(execute_file)를 연결해주고,
    #   테이블의 헤더를 클릭 했을 때 정렬 기능을 실행하기 위한 이벤트(sort_data)도 연결해준다.
    ###################################################################################################################
    def initTable(self):
        self.header_sorted_state = [False, False, False]
        self.table_model = TableModel()

        self.tableview.setSortingEnabled(True)

        self.tableview.doubleClicked.connect(self.execute_file)
        self.tableview.horizontalHeader().sectionClicked.connect(self.sort_data)

    ####################################################################################################################
    # inintUI
    # = UI를 초기화하기 위한 메소드이다.
    #   사용자 입력을 받기 위한 LineEdit, 검색된 결과 개수를 표시하기 위한 Label, 검색된 결과를 표시하기 위한 tableview
    #   위젯들을 포함한다.
    ####################################################################################################################
    def initUI(self):

        # 사용자 입력을 받는 위젯
        self.qle = QLineEdit(self)
        self.qle.textEdited.connect(self.update_table_data)

        # 검색된 결과의 개수를 표시하는 위젯
        self.label = QLabel(self)

        # 검색된 결과를 표시하는 테이블
        self.tableview = QTableView()
        self.initTable()

        # UI 구성
        self.vbox = QVBoxLayout()
        self.vbox.addWidget(self.qle)
        self.vbox.addWidget(self.tableview)
        self.vbox.addWidget(self.label)

        self.setLayout(self.vbox)
        self.setWindowTitle('File Search Program')
        self.setGeometry(300, 100, 1500, 800)
        self.show()

    ####################################################################################################################
    # displayFiles
    # - file_list : 테이블 상에 표시한 파일들의 정보를 담고 있는 리스트 ( 파일명, 경로, 크기를 튜플 형태로 저장)
    # = 검색 결과에 의해 갱신되는 테이블을 나타내는 메소드
    ####################################################################################################################
    def displayFiles(self, file_list):
        total = len(file_list)
        self.label.setText("총 {0} 개 검색됨".format(total))

        self.tableview.clearSpans()
        self.table_model.layoutAboutToBeChanged.emit()
        self.table_model.setData(file_list)
        self.table_model.layoutChanged.emit()

        self.tableview.setModel(self.table_model)
        self.tableview.setColumnWidth(0, 600)
        self.tableview.setColumnWidth(1, 600)
        self.tableview.setColumnWidth(2, 80)

    ########################################################################################################################
    # start_thread
    # - mode : 시작하게 될 쓰레드를 명시하기 위한 변수
    # = 인자로 받은 mode을 통해 실행할 쓰레드를 결정하고, 쓰레드를 실행한다.
    ########################################################################################################################
    def start_thread(self, mode):
        if mode == 0: #read_db_thread -> DB로부터 파일 정보 읽기
            self.read_db_thread.start()

        elif mode == 1: #scan_thread -> 파일 시스템 스캔 및 DB 저장
            self.scan_thread.start()

        elif mode == 2: #manager_observer_thread -> 파일 변화를 감지하는 Observer를 thread을 생성하고, 이벤트 처리
            self.manager_observer_thread.start()

        elif mode == 3: #insert_db_thread -> DB에 추가된 파일 정보 저장
            self.insert_db_thread.start()

        elif mode == 4: #delete_db_thread -> DB에서 삭제된 파일 정보 제거
            self.delete_db_thread.start()

    ########################################################################################################################
    # insert_fileinfo
    # - file_info : 추가하게 될 파일 정보가 들어있는 리스트(파일명, 경로, 사이즈)
    # = 인자로 받은 file_info를 통해 파일 정보를 갱신하고, 테이블을 갱신하는 메소드를 호출한다.
    ########################################################################################################################
    def insert_fileinfo(self, file_info):
        self.cached_file_list.append(file_info)
        self.check_time.start()

    ########################################################################################################################
    # delete_fileinfo
    # - file_info : 삭제하게 될 파일 정보가 들어있는 리스트(파일명, 경로)
    # = 인자로 받은 file_info를 통해 파일명과 경로명이 일치하는 파일 정보를 삭제하고, 테이블을 갱신하는 메소드를 호출한다.
    ########################################################################################################################
    def delete_fileinfo(self, file_info):
        file_name = file_info[0]
        dir_name = file_info[1]

        for f in self.cached_file_list:
            if f[0] == file_name and f[1] == dir_name:
                self.cached_file_list.remove(f)
                break

        self.check_time.start()

    def processing_time_out(self):
        self.update_table_data(True)

########################################################################################################################
#     이벤트 핸들러 메소드
########################################################################################################################

    ####################################################################################################################
    # sort_data
    # - column_index : 선택 된 헤더의 column_index
    # = 헤더(File, Path, Size(kb))를 클릭했을 때 정렬을 수행하기 위한 이벤트 처리 메소드
    #   각 헤더 마다 가지고 있는 header_sorted_state 정보를 통해 해당 열에 대한 정렬 기준을 수립한다.
    #   False -> 오름 차순으로, True -> 내림 차순으로 적용
    ####################################################################################################################
    @pyqtSlot(int)
    def sort_data(self, column_index):
        self.table_model.layoutAboutToBeChanged.emit()
        table_data = self.table_model.getData()

        if self.header_sorted_state[column_index]: # 내림 차순
            table_data = sorted(table_data, key=lambda f: f[column_index] if column_index==2 else f[column_index].upper(), reverse = True)
            self.header_sorted_state[column_index] = False
        else: # 오름 차순
            table_data = sorted(table_data, key = lambda f: f[column_index] if column_index==2 else f[column_index].upper())
            self.header_sorted_state[column_index] = True

        self.table_model.setData(table_data)
        self.table_model.layoutChanged.emit()

    ####################################################################################################################
    # execute_file
    # - signal : 선택된 tableview의 entry에 대한 정보를 담고 있는 QModeIndex 객체
    # = 테이블 상에서 entry을 더블 클릭했을 때 파일 실행 또는 디렉토리 열기를 수행하기 위한 이벤트 처리 메소드
    #   경로를 더블 클릭 시 -> 해당 디렉토리 열기
    #   파일명 또는 사이즈를 더블 클릭 시 -> 해당 파일 실행
    ####################################################################################################################
    @pyqtSlot(QModelIndex)
    def execute_file(self, signal):
        row = signal.row()
        col = signal.column()

        if col == 1: # 파일 경로 더블 클릭 -> 해당 디렉토리 열기
            index = signal.sibling(row, col)
            index_dict = self.table_model.itemData(index)
            path = index_dict.get(0)
        else: # 파일 명 더블 클릭 -> 해당 파일 실행
            findex = signal.sibling(row, 0)
            findex_dict = self.table_model.itemData(findex)
            file_name = findex_dict.get(0)

            pindex = signal.sibling(row, 1)
            pindex_dict = self.table_model.itemData(pindex)
            path = pindex_dict.get(0)
            path = path + "\\" + file_name

        webbrowser.open(path)

    ####################################################################################################################
    # finish_scan
    # = scan_thread의 동작이 완료되었을 때을 위한 이벤트 처리 메소드
    #   start_thread 메소드를 통해 파일 변화를 감지하는 manager_observer_thread를 실행한다.
    #   또한 파일 시스템 스캔을 통해 얻은 파일 정보를 cached_file_list에 갱신한다.
    #   사용자 입력값에 따라 변경사항을 테이블에 표시한다.
    ####################################################################################################################
    @pyqtSlot(list)
    def finish_scan(self, file_list):
        self.finish_scan_flag = True
        self.cached_file_list = file_list
        len(self.cached_file_list)
        self.update_table_data()

    ########################################################################################################################
    # finish_read
    # = read_db_thread 에 의해 프로그램 시작 후, DB에서 파일 정보를 읽어왔을 때 시그널을 받게 되는 메소드
    #   수집된 파일 정보를 나타내는 cached_file_list를 갱신하고, 해당 값을 테이블에 표시한다.
    ########################################################################################################################
    @pyqtSlot(list)
    def finish_read(self, file_list):
        self.cached_file_list = file_list
        self.displayFiles(file_list)

    ########################################################################################################################
    # update_table_data
    # = 사용자 입력값 변경 시 또는 파일 정보에 변화가 생겼을 때, 입력값을 기준으로 테이블에 표시할 파일 정보를 생성하는 메소드
    #   생성된 파일 정보를 displayFiles 메소드를 통해 테이블 나타낸다.
    ########################################################################################################################
    @pyqtSlot()
    def update_table_data(self, time_out = False):
        text = self.qle.text().upper()

        if self.count < 2 and len(text) > 0 and not time_out:
            self.count = self.count + 1
            self.check_time.start()
            return

        self.count = 0

        # file_list의 첫번째 항목인 file_name을 토대로 text 값을 포함하고 있으면 displayed_file_list에 추가한다.
        displayed_file_list = [file_info for file_info in self.cached_file_list if text in file_info[0].upper()]
        self.displayFiles(displayed_file_list)

    ########################################################################################################################
    # control_updated_file
    # - file_infos : 파일 변경 감지에 따라 대상이 되는 파일 정보가 저장되어있는 리스트이며
    #   0번 인덱스는 실질적인 파일 정보가 들어가 있고,
    #   1번 인덱스에는 어떠한 파일 변경 이벤트가 발생했는지를 나타낸다.
    # = file_infos를 통해 파일 변경 이벤트를 구분하고, 각 이벤트에 맞게 테이블 갱신 및 DB 갱신을 수행하는 메소드를 호출한다.
    ########################################################################################################################
    @pyqtSlot(list)
    def control_updated_file(self, file_infos):
        mode = file_infos[1]
        file_info = file_infos[0]

        if mode == 3: # insert
            self.insert_fileinfo(file_info)
            self.insert_db_thread.set_file_info(file_info)

        elif mode == 4: # delete
            self.delete_fileinfo(file_info)
            self.delete_db_thread.set_file_info(file_info)

        self.start_thread(mode)

########################################################################################################################
# TableModel 클래스
# QAbstractTableModel 인터페이스를 상속받아 세부 메소드를 구현하고 있다.
# View - Model 관점에서 View는 단지 데이터를 나타내기 위한 객체이며, Model은 데이터 처리 또는 정의를 나타내는 객체이다.
# 이러한 Model의 객체에 대한 정의를 내리는 클래스이다.
########################################################################################################################
class TableModel(QAbstractTableModel):
    header_labels = ['Name', 'Path', 'Size(KB)']

    def __init__(self):
        super(TableModel, self).__init__()

    def setData(self, filelist):
        self._data = filelist

    def getData(self):
        return self._data

    def data(self, index, role):
        if role == Qt.DisplayRole:
            return self._data[index.row()][index.column()]

    def headerData(self, section, orientation, role = Qt.DisplayRole):
        if role == Qt.DisplayRole and orientation == Qt.Horizontal:
            return self.header_labels[section]
        return QAbstractTableModel.headerData(self, section, orientation, role)

    def rowCount(self, index):
        return len(self._data)

    def columnCount(self, index):
        return 3

########################################################################################################################
# is_admin
# = 프로그램이 관리자 권한으로 실행되었는지 확인하기 위한 함수
########################################################################################################################
def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

if __name__ == '__main__':
    freeze_support()

    # 실행 파일을 권리자 권한으로 수행하기 위해 사용하는 코드
    """
    if not is_admin():
        script = os.path.abspath(sys.argv[0])
        params = ' '.join([script] + sys.argv[1:])

        shell.ShellExecuteEx(nShow = 1, lpVerb = 'runas', lpFile = sys.executable, lpParameters=params)
        sys.exit()
    """

    try:
        app = QApplication(sys.argv)
        ex = UI()
        sys.exit(app.exec_())
    finally:
        pid = os.getpid()
        spid = str(pid)

        subprocess.run(args = ['taskkill', '/F', '/T', '/PID', spid], shell = False)