#coding: utf-8

# Threadpool - 여러 스레드를 특정 프로세스에 사용하도록 유지한다.
# 스레드 재사용에 목적이 있고 불필요한 스레드 생성을 방지

import sys, logging, re
import threading, queue, requests, concurrent.futures

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(message)s')

ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
logger.addHandler(ch)

html_link_regex = \
     re.compile('<a\s(?:.*?\s)*?href=[\'"](.*?)[\'"].*?>')

urls = queue.Queue()
urls.put('http://www.google.com')
urls.put('http://br.bing.com/')
urls.put('https://duckduckgo.com/')
urls.put('https://github.com/')
urls.put('http://br.search.yahoo.com/')

result_dict = {}

# 동기화된 큐로부터 URL를 추출해 result_dict 넣는다.
def group_urls_task(urls):
    try:
        # True는 동기화된 큐에 접근을 차단
        url = urls.get(True, 0.05)
        result_dict[url] = None
        logger.info("[%s] putting url [%s] in dictionary..." % (
            threading.current_thread().name, url))
    except queue.Empty:
        logging.error('Nothing to be done, queue is empty')

# 페이지의 링크 수집
def crawl_task(url):
    links = []

    try:
        request_data = requests.get(url)
        logger.info("[%s] crawling url [%s] ..." % (
            threading.current_thread().name, url))
        links = html_link_regex.findall(request_data.text)
    except:
        logger.error(sys.exc_info()[0])
    finally:
        return (url, links)

# ThreadPoolExecutor에 정의된 worker 스레드가 비동기 방식인 병렬로 수행한다.
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as group_link_threads:
    for i in range(urls.qsize()):
        group_link_threads.submit(group_urls_task, urls)

with concurrent.futures.ThreadPoolExecutor(max_workers=3) as crawler_link_threads:
    future_tasks = {crawler_link_threads.submit(crawl_task, url): url for url in result_dict.keys()}
    for future in concurrent.futures.as_completed(future_tasks):
        result_dict[future.result()[0]] = future.result()[1]

for url, links in result_dict.items():
    logger.info("[%s] with links : [%s..." % (url, links[0]))


from PIL import Image
import argparse

#构建ArgumentParser实例，用于处理命令行输入参数
parser = argparse.ArgumentParser()

#定义输入文件，输出文件，输出字符画的宽和高
parser.add_argument('file')
parser.add_argument('-o','--output')
parser.add_argument('--width',type = int ,default= 80)
parser.add_argument('--height',type = int, default= 80)

#解析并获取参数
args = parser.parse_args()

#输入的图片文件路径
IMG = args.file

#输出字符画的宽度
WIDTH = args.width

#输出字符画的高度
HEIGHT = args.height

#输出字符画的路径
OUTPUT = args.output

#字符画使用的字符集
ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")

#RGB转字符串的函数
def get_char(r,g,b,alpha = 256):

    #判断 alpha 值
    if alpha == 0:
        return ' '

    #获取字符集的长度，这里为70
    length = len(ascii_char)

    #将RGB值转为灰度值gray, 灰度值范围为0~255
    gray = int(0.2126 * r + 0.7152 * g + 0.0722 *b)

    #灰度值范围为256，而字符集只有70
    unit = (256.0 + 1)/length

    #返回灰度值对应的字符
    return ascii_char[int(gray/unit)]

if __name__ == '__main__':

    #打开并调整图片的宽和高
    im = Image.open(IMG)
    im = im.resize((WIDTH,HEIGHT), Image.NEAREST)

    #初始化输出的字符串
    txt = ""

    #遍历图片中的每一行
    for i in range(HEIGHT):
        #遍历图片中的每一列
        for j in range(WIDTH):
            txt += get_char(*im.getpixel((j,i)))
        txt += '\n'
    print(txt)

    #字符画输出到文件
    if OUTPUT:
        with open(OUTPUT,'w') as f:
            f.write(txt)
    else:
        with open("output.txt,'w") as f:
            f.write(txt)


import numpy as np
from scipy.io import loadmat
import pandas as pd
import datetime as date
from dateutil.relativedelta import relativedelta

cols = ['age', 'gender', 'path', 'face_score1', 'face_score2']

imdb_mat = 'imdb_crop/imdb.mat'
wiki_mat = 'wiki_crop/wiki.mat'

imdb_data = loadmat(imdb_mat)
wiki_data = loadmat(wiki_mat)

del imdb_mat, wiki_mat

imdb = imdb_data['imdb']
wiki = wiki_data['wiki']

imdb_photo_taken = imdb[0][0][1][0]
imdb_full_path = imdb[0][0][2][0]
imdb_gender = imdb[0][0][3][0]
imdb_face_score1 = imdb[0][0][6][0]
imdb_face_score2 = imdb[0][0][7][0]

wiki_photo_taken = wiki[0][0][1][0]
wiki_full_path = wiki[0][0][2][0]
wiki_gender = wiki[0][0][3][0]
wiki_face_score1 = wiki[0][0][6][0]
wiki_face_score2 = wiki[0][0][7][0]

imdb_path = []
wiki_path = []

for path in imdb_full_path:
    imdb_path.append('imdb_crop/' + path[0])

for path in wiki_full_path:
    wiki_path.append('wiki_crop/' + path[0])

imdb_genders = []
wiki_genders = []

for n in range(len(imdb_gender)):
    if imdb_gender[n] == 1:
        imdb_genders.append('male')
    else:
        imdb_genders.append('female')

for n in range(len(wiki_gender)):
    if wiki_gender[n] == 1:
        wiki_genders.append('male')
    else:
        wiki_genders.append('female')

imdb_dob = []
wiki_dob = []

for file in imdb_path:
    temp = file.split('_')[3]
    temp = temp.split('-')
    if len(temp[1]) == 1:
        temp[1] = '0' + temp[1]
    if len(temp[2]) == 1:
        temp[2] = '0' + temp[2]

    if temp[1] == '00':
        temp[1] = '01'
    if temp[2] == '00':
        temp[2] = '01'

    imdb_dob.append('-'.join(temp))

for file in wiki_path:
    wiki_dob.append(file.split('_')[2])

imdb_age = []
wiki_age = []

for i in range(len(imdb_dob)):
    try:
        d1 = date.datetime.strptime(imdb_dob[i][0:10], '%Y-%m-%d')
        d2 = date.datetime.strptime(str(imdb_photo_taken[i]), '%Y')
        rdelta = relativedelta(d2, d1)
        diff = rdelta.years
    except Exception as ex:
        print(ex)
        diff = -1
    imdb_age.append(diff)

for i in range(len(wiki_dob)):
    try:
        d1 = date.datetime.strptime(wiki_dob[i][0:10], '%Y-%m-%d')
        d2 = date.datetime.strptime(str(wiki_photo_taken[i]), '%Y')
        rdelta = relativedelta(d2, d1)
        diff = rdelta.years
    except Exception as ex:
        print(ex)
        diff = -1
    wiki_age.append(diff)

final_imdb = np.vstack((imdb_age, imdb_genders, imdb_path, imdb_face_score1, imdb_face_score2)).T
final_wiki = np.vstack((wiki_age, wiki_genders, wiki_path, wiki_face_score1, wiki_face_score2)).T

final_imdb_df = pd.DataFrame(final_imdb)
final_wiki_df = pd.DataFrame(final_wiki)

final_imdb_df.columns = cols
final_wiki_df.columns = cols

meta = pd.concat((final_imdb_df, final_wiki_df))

meta = meta[meta['face_score1'] != '-inf']
meta = meta[meta['face_score2'] == 'nan']

meta = meta.drop(['face_score1', 'face_score2'], axis=1)

meta = meta.sample(frac=1)

meta.to_csv('meta.csv', index=False)


from typing import Counter
from collections import deque
from graph_list import Graph

def topological_sort(gph):
    stk = []
    count = gph.count
    visited = [False] * count
    for i in range(count):
        if visited[i] == False:
            topological_sort_dfs(gph, i, visited, stk)
    print("Topological Sort :: ", end=' ')
    while len(stk) != 0:
        print(stk.pop(), end=' ')

def topological_sort_dfs(gph, index, visited, stk):
    visited[index] = True
    for edge in gph.adj[index]:
        destination = edge[0]
        if visited[destination] == False:
            topological_sort_dfs(gph, destination, visited, stk)
    stk.append(index)

def path_exists(gph, source, destination):
    count = gph.count
    visited = [False] * count
    dfs_util(gph, source, visited)
    return visited[destination]

def dfs_util(gph, index, visited):
    visited[index] = True
    for edge in gph.adj[index]:
        destination = edge[0]
        if visited[destination] == False:
            dfs_util(gph, destination, visited)

def count_all_paths_dfs(gph, visited, source, dest):
    if source == dest:
        return 1
    count = 0
    visited[source] = 1
    for edge in gph.adj[source]:
        if visited[edge[0]] == 0:
            count += count_all_paths_dfs(gph, visited, edge[0], dest)
    visited[source] = 0
    return count

def print_all_paths_dfs(gph, visited, source, dest, path):
    path.append(source)
    if source == dest:
        print(path)
        return
    visited[source] = 1
    for edge in gph.adj[source]:
        if visited[edge[0]] == 0:
            print_all_paths_dfs(gph, visited, edge[0], dest, path)
    visited[source] = 0
    path.pop()

def root_vertex(gph):
    count = gph.count
    visited = [False] * count
    retVal = -1
    for i in range(count):
        if visited[i] == False:
            dfs_util(gph, i, visited)
            retVal = i
    print("Root vertex is :: ", retVal)

def transitive_closure_util(gph, source ,index, tc):
    if tc[source][index] == 1:
        return
    tc[source][index] = 1
    for edge in gph.adj[index]:
            transitive_closure_util(gph, source, edge[0], tc)

def transitive_closure(gph):
    count = gph.count
    tc = [[0 for _ in range(count)] for _ in range(count)]
    for source in range(count):
        transitive_closure_util(gph, source, source, tc)
    for row in tc:
        print(row)
    return tc

def bfs_distance(gph, source, dest):
    count = gph.count
    visited = [False] * count
    visited[source] = True
    que = deque([])
    que.append((source, 0))
    while len(que) != 0:
        node = que.popleft()
        curr = node[0]
        depth = node[1]
        for edge in gph.adj[curr]:
            if edge[0] == dest:
                return depth+1
            if visited[edge[0]] == False:
                que.append((edge[0], depth+1))
                visited[edge[0]] = True
    return -1

def bfs_level_node(gph, source):
    count = gph.count
    visited = [False] * count
    visited[source] = True
    que = deque([])
    que.append((source, 0))
    print("\nNode  - Level")
    while len(que) != 0:
        node = que.popleft()
        curr = node[0]
        depth = node[1]
        print(curr ," - ", depth)
        for edge in gph.adj[curr]:
            destination = edge[0]
            if visited[destination] == False:
                que.append((destination, depth+1))
                visited[destination] = True

def is_cycle_present_undirected_dfs(graph, index, parentIndex, visited):
    visited[index] = True
    for node in graph.adj[index]:
        dest = node[0]
        if visited[dest] == False:
            if is_cycle_present_undirected_dfs(graph, dest, index, visited) :
                return True
        elif parentIndex != dest :
            return True
    return False

def is_cycle_present_dfs(graph, index, visited, marked):
    visited[index] = True
    marked[index] = True
    for node in graph.adj[index]:
        dest = node[0]
        if marked[dest] == True:
            return True
        if visited[dest] == False:
            if is_cycle_present_dfs(graph, dest, visited, marked) :
                return True
    marked[index] = False
    return False

def transpose_graph(gph):
    count = gph.count
    g = Graph(count)
    for i in range(count):
        for edge in gph.adj[i]:
            destination = edge[0]
            g.add_direct_edge(destination, i)
    return g

def is_connected_undirected(gph):
    count = gph.count
    visited = [False] * count
    dfs_util(gph, 0, visited)
    for i in range(count):
        if visited[i] == False:
            return False
    return True

def is_strongly_connected(gph):
    count = gph.count
    visited = [False] * count
    dfs_util(gph, 0, visited)
    for i in range(count):
        if visited[i] == False:
            return False
    graph_reversed = transpose_graph(gph)
    visited = [False] * count
    dfs_util(graph_reversed, 0, visited)
    for i in range(count):
        if visited[i] == False:
            return False
    return True

def dfs_util2(gph, index, visited, stk):
    visited[index] = True
    for edge in gph.adj[index]:
        destination = edge[0]
        if visited[destination] == False:
            dfs_util2(gph, destination, visited, stk)
    stk.append(index)

def strongly_connected_component(gph):
    count = gph.count
    visited = [False] * count
    stk = []
    for i in range(count):
        if visited[i] == False:
            dfs_util2(gph, i, visited, stk)

    graph_reversed = transpose_graph(gph)
    visited = [False] * count
    while len(stk) != 0:
        index = stk.pop()
        if visited[index] == False:
            stk2 = []
            dfs_util2(graph_reversed, index, visited, stk2)
            print(stk2)

def primsMST(gph):
    previous = [-1] * gph.count
    dist = [sys.maxsize] * gph.count
    visited = [False] * gph.count
    source = 0
    dist[source] = 0
    previous[source] = -1
    pq = PriorityQueue()
    for i in range(gph.count):
        pq.add(sys.maxsize, i)
    pq.update_key(0, source)

    while pq.size() != 0:
        val = pq.pop()
        source = val[1]
        visited[source] = True

        for edge in gph.adj[source]:
            destination = edge[0]
            cost = edge[1]
            if cost < dist[destination] and visited[destination] == False:
                dist[destination] = cost
                previous[destination] = source
                pq.update_key(cost, destination)

    total_cost = 0

    for i in range(gph.count):
        if dist[i] == sys.maxsize:
            print("node id" , i , "prev" , previous[i] , "distance : Unreachable")
        else:
            print("node id" , i , "prev" , previous[i] , "distance :" , dist[i])
            total_cost += dist[i]

    print("Total MST cost: ", str(total_cost))


import sys
from asyncio import FastChildWatcher
import json
import dateutil.parser
import babel
from flask import Flask, render_template, request, Response, flash, redirect, url_for
from flask_moment import Moment
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
import logging
from logging import Formatter, FileHandler
from flask_wtf import Form
from sqlalchemy import true
from forms import *
from flask_wtf.csrf import CsrfProtect
from datetime import datetime
#from jinja2.utils import markupsafe
#from markupsafe import Markup
#markupsafe.Markup()
#Markup()

app = Flask(__name__)
moment = Moment(app)
app.config.from_object('config')
CsrfProtect(app)

db = SQLAlchemy(app)
migrate = Migrate(app, db)

# TODO: connect to a local postgresql database

#----------------------------------------------------------------------------#
# Models.
#----------------------------------------------------------------------------#

class Venue(db.Model):
    __tablename__ = 'Venue'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    city = db.Column(db.String(120))
    state = db.Column(db.String(120))
    address = db.Column(db.String(120))
    phone = db.Column(db.String(120))
    image_link = db.Column(db.String(500))
    facebook_link = db.Column(db.String(120))
    website_address = db.Column(db.String(150))
    shows = db.relationship('Show')
    artists = db.relationship(
      'Artists',
      secondary = 'shows',
      back_populates = 'venues'
      )
    description_venue = db.Column(db.String(1000))
    talent_to_Show = db.Column(db.Boolean, default = False)

    # TODO: implement any missing fields, as a database migration using Flask-Migrate

class Artist(db.Model):
    __tablename__ = 'Artist'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    city = db.Column(db.String(120))
    state = db.Column(db.String(120))
    phone = db.Column(db.String(120))
    genres = db.Column(db.String(120))
    image_link = db.Column(db.String(500))
    facebook_link = db.Column(db.String(120))
    website_address = db.Column(db.String(150))
    description_artist = db.Column(db.String(1000))
    seek_venue = db.Column(db.Boolean, default=False)
    shows = db.relationship('Show')
    venues = db.relationship(
      'Venue',
      secondary = 'shows',
      back_populates ='artists')

class Show(db.Model):
  __tablename__ = 'shows'

  id = db.Column(db.Integer, primary_key=True)
  start_time = db.Column(db.DateTime, nullable=False)
  end_time = db.Column(db.DateTime, nullable = False)

  venue = db.relationship('Venue')
  venue_id = db.Column(
    db.Integer,
    db.foreignKey('venues_id',ondelete = 'CASCADE'),
    nullable = False
    )

  artist=db.relationship('Artists')
  artist_id = db.Column(
    db.Integer,
    db.foreignKey('artists_id', ondelete = 'CASCADE'),
    nullable = False
  )

    # TODO: implement any missing fields, as a database migration using Flask-Migrate

# TODO Implement Show and Artist models, and complete all model relationships and properties, as a database migration.

#----------------------------------------------------------------------------#
# Filters.
#----------------------------------------------------------------------------#

def format_datetime(value, format='medium'):
  date = dateutil.parser.parse(value)
  if format == 'full':
      format="EEEE MMMM, d, y 'at' h:mma"
  elif format == 'medium':
      format="EE MM, dd, y h:mma"
  return babel.dates.format_datetime(date, format, locale='en')

app.jinja_env.filters['datetime'] = format_datetime

#----------------------------------------------------------------------------#
# Controllers.
#----------------------------------------------------------------------------#

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

#  Venues
#  ----------------------------------------------------------------

@app.route('/venues')
def venues():
  data_areas = []
  # TODO: replace with real venues data.
  areas = Venue.query.with_entities(Venue.city, Venue.state).group_by(Venue.city,Venue.state).all()

  for area in areas:
    data_venues = []
    venues = Venue.query.filter_by(state = area.state).filter_by(city=area.city).all()

    for venue in venues:
      upcomming_shows = db.session.query(Show).filter(Show.venue_id==venue.id).filter(Show.start_time> datetime.now).all()
      data_venues.append({'id':venue.id,
                          'name':venue.name,
                          'num_upcoming _shows': len(upcomming_shows)})

    data_areas.append({'city':area.city,
                  'state':area.state,
                  'venues':data_venues})

  return render_template('pages/venues.html', areas=data_areas)

@app.route('/venues/search', methods=['POST'])
def search_venues():
  # TODO: implement search on venues with partial string search. Ensure it is case-insensitive.
  # seach for Hop should return "The Musical Hop".
  # search for "Music" should return "The Musical Hop" and "Park Square Live Music & Coffee"
  search_term = request.form['search_term']
  search = "%{}%".format(search_term)

  venues = Venue.query.with_entities(Venue.id, Venue.name).fileter(Venue.name.natch(search)).all()
  data_venues = []
  for venue in venues:
    #upcomming shows by venue
    upcoming_shows =db.session.quesry(Show).filter(Show.venue_id ==venue.id).filter(Show.start_time>datetime.now()).all()
    data_venues.append({'id':venue.id, 'name':venue.name, 'num_upcoming_shows':len(upcoming_shows)})
  results = {'venues':data_venues, 'count':len(venues)}
  return render_template('pages/search_venues.html', results=results, search_term=search_term)

@app.route('/venues/<int:venue_id>')
def show_venue(venue_id):
  # shows the venue page with the given venue_id
  # TODO: replace with real venue data from the venues table, using venue_id
  data_venue = Venue.query.filter(Venue.id==venue_id).first()
  upcoming_shows = Show.query.filter(Show.venue_id == venue_id).filter(Show.start_time>datetime.now()).all()

  if len(upcoming_shows>0):
    data_upcoming_shows = []
    for upcoming_show in upcoming_shows:
      artist = Artist.query.filter(Artist.id == upcoming_show.artist_id).first()
      data_upcoming_shows.append({'artist_id': artist.id, 'artist_name':artist.name, 'artist_image_link':artist.image_link,
                                  'start_time': str(upcoming_show.start_time)})

    data_venue.upcoming_shows = data_upcoming_shows
    data_venue.upcoming_shows_count = len(data_upcoming_shows)

    # past show of this venue
    past_shows =Show.query.filter(Show.venue_id == venue_id).filter(Show.start_time<datetime.now()).all()

    if len(past_shows>0):
      data_past_shows = []
      for past_show in past_shows:
        artist = Artist.query.filter(Artist.id ==past_show.artist_id).first()
        data_past_shows.append({'artist_id':artist.id, 'artist_name':artist.name,'artist_image_link':artist.image_link, 'start_time': str(past_show.start_time)})
      data_venue.past_shows = data_past_shows
      data_venue.past_shows_count = len(data_past_shows)

  return render_template('pages/show_venue.html', venue = data_venue)

#  Create Venue
#  ----------------------------------------------------------------

@app.route('/venues/create', methods=['GET'])
def create_venue_form():
  form = VenueForm()
  return render_template('forms/new_venue.html', form=form)

@app.route('/venues/create', methods=['POST'])
def create_venue_submission():
  # TODO: insert form data as a new Venue record in the db, instead
  # TODO: modify data to be the data object returned from db insertion

  error =False
  form = VenueForm()

  #validate form
  if not form.validate():
    #for fieldName, errorMessages in form.errors.items():
      #show_form_errors(fieldName,errorMessages)
    return redirect(url_for('create_venue_form'))
  #data
  name = request.form('name')
  city = request.form('city')
  state = request.form('state')
  address = request.form('address')
  phone = request.form('phone')
  genres = request.form.getlist('genres')
  image_link = request.form('image_link')
  facebook_link = request.form('facebook_link')
  website = request.form('website')
  seeking_talent = True if 'seeking_talent' in request.form else False
  seeking_description = request.form('seeking_description')

  try:
    venue = Venue(name=name, city = city, state =state, address=address,
                  phone=phone, genres =genres, image_link=image_link,
                  facebook_link=facebook_link,website=website,
                  seeking_talent = seeking_talent, seeking_description= seeking_description)
    db.session.add(venue)
    db.session.commit()
  except Exception:
    error=True
    db.session.rollback()
    print(sys.exc_info())
  finally:
    db.session.close()
  if error:
    #abort(404)
    flash('An error occured. Venue' + name +' is not available')
  if not error:
    flash('Venue'+ name+ ' discovered')

  return render_template('pages/home.html')
  # on successful db insert, flash success
  #flash('Venue ' + request.form['name'] + ' was successfully listed!')
  # TODO: on unsuccessful db insert, flash an error instead.
  # e.g., flash('An error occurred. Venue ' + data.name + ' could not be listed.')
  # see: http://flask.pocoo.org/docs/1.0/patterns/flashing/

@app.route('/venues/<venue_id>', methods=['DELETE'])
def delete_venue(venue_id):
  # TODO: Complete this endpoint for taking a venue_id, and using
  error =False
  try:
    Venue.query.filter_by(id=venue_id).delete()
    db.session.commit()
  except Exception:
    error = True
    db.session.rollback()
    print(sys.exc_info())
    return render_template('errors/500.html')
  finally:
    db.session.close()

  if error:
    #abort(404)
    flash('Venue can not be deleted')
  if not error:
    flash('Venue was deleted')
  return render_template('pages/home.html')

  # SQLAlchemy ORM to delete a record. Handle cases where the session commit could fail.

  # BONUS CHALLENGE: Implement a button to delete a Venue on a Venue Page, have it so that
  # clicking that button delete it from the db then redirect the user to the homepage
  #return None

#  Artists
#  ----------------------------------------------------------------
@app.route('/artists')
def artists():
  # TODO: replace with real data returned from querying the database
  data_artists = []

  artists = Artist.quesry.with_entities(Artist.id, Artist.name).order_by('id').all()
  for artist in artists:
    upcomming_shows = db.session.query(Show).filter(Show.artist_id == artist.id).filter(Show.start_time>datetime.now()).all()
    data_artists.append({'id':artist.id, 'name':artist.name, 'num_upcoming_shows':len(upcomming_shows)})
  return render_template('pages/artists.html', artists = data_artists)

@app.route('/artists/search', methods=['POST'])
def search_artists():

  search_term = request.form('search_term')
  search = "%{}%".format(search_term)
  artists = Artist.query.with_entities(Artist.id, Artist.name).filter(Artist.name.match(search)).all()
  data_artists = []
  for artist in artists:
    upcoming_shows = db.session.query(Show).filter(Show.artist_id ==artist.id).filter(Show.start_time>datetime.now()).all()
  data_artists.append({'id':artist.id, 'name':artist.name,'num_upcoming_shows':len(upcoming_shows)})

  return render_template('pages/search_artists.html')

@app.route('/artists/<int:artist_id>')
def show_artist(artist_id):
  data_artist = Artist.query.filter(Artist.id ==artist_id)
  upcoming_shows = Show.query.filter(Show.artist_id == artist_id).filter(Show.start_time>datetime.now()).all()
  if len(upcoming_shows)>0:
    data_upcoming_shows = []
    for upcoming_show in upcoming_shows:
      venue = Venue.query.filter(Venue.id == upcoming_show.venue_id).first()
      data_upcoming_shows.append({'venue_id':venue.id, 'venue_name':venue.name,
                                  'venue_image_link': venue.image_link, 'start_time':str(upcoming_show.start_time)})
    data_artist.upcoming_shows = data_upcoming_shows
    data_artist.upcoming_shows_count = len(data_upcoming_shows)

  past_shows = Show.query.filter(Show.artist_id ==artist_id).filter(Show.start_time<datetime.now()).all()
  if len(past_shows)>0:
    data_past_shows = []
    for past_show in past_shows:
      venue = Venue.query.filter(Venue.id ==upcoming_show.venue_id).first()
      data_past_shows.append({'venue_id':venue.id, 'venue_name':venue.name,
                              'venue_image_link':venue.image_link, 'start_time':str(past_show.start_time)})

    data_artist.past_shows = data_past_shows
    data_artist.past_shows_count = len(data_past_shows)
  return render_template('pages/show_artist.html', artist = data_artist)

  # shows the artist page with the given artist_id
  # TODO: replace with real artist data from the artist table, using artist_id

#  Update
#  ----------------------------------------------------------------
@app.route('/artists/<int:artist_id>/edit', methods=['GET'])
def edit_artist(artist_id):
  artist = Artist.query.filter(Artist.id ==artist_id).first()
  form = ArtistForm()

  form.name.data = artist.name
  form.city.data = artist.city
  form.state.data = artist.city
  form.phone.data = artist.phone
  form.genres.data = artist.genres
  form.image_link.data = artist.image_link
  form.facebook_link.data = artist.facebook_link
  form.website.data = artist.website
  form.seeking_venue.data = artist.seeking_venue
  form.seeking_description.data = artist.seeking_description

  return render_template('forms/edit_artist.html', form = form, artist = artist)

@app.route('/artists/<int:artist_id>/edit', methods=['POST'])
def edit_artist_submission(artist_id):

  error = False
  name = request.form['name']
  city = request.form['city']
  state = request.form['state']
  phone = request.form['phone']
  genres = request.form.getlist('genres')
  image_link = request.form['image_link']
  facebook_link = request.form['facebook_link']
  website = request.form['website']
  seeking_talent = True if 'seeking_talent' in request.form else False
  seeking_description = request.form['seeking_description']

  try:
    artist = Artist.query.get(artist_id)
    artist.name = name
    artist.city = city
    artist.state = state
    artist.phone = phone
    artist.genres = genres
    artist.image_link = image_link
    artist.facebook_link = facebook_link
    artist.website = website
    artist.seeking_talent = seeking_talent
    artist.seeking_description = seeking_description

    db.session.commit()
  except Exception:
    error = True
    db.session.roleback()
    print(sys.exc_info)
  finally:
    db.session.close()
  if error:
    #abort(400)
    flash('Artist'+name+' coud not be updated')
  if not error:
    flash('Artist' + name + 'updated successfully')

  return redirect(url_for('show_artist', artist_id=artist_id))

@app.route('/venues/<int:venue_id>/edit', methods=['GET'])
def edit_venue(venue_id):
  venue = Venue.query.filter(Venue.id == venue_id).first()
  form = VenueForm()

  form.name.data = venue.name
  form.city.data = venue.city
  form.state.data = venue.state
  form.address.data = venue.address
  form.phone.data = venue.phone
  form.genres.data = venue.genres
  form.image_link.data = venue.image_link
  form.facebook_link.data = venue.facebook_link
  form.website.data = venue.website
  form.seeking_talent.data = venue.seeking_talent
  form.seeking_description.data = venue.seeking_description

  return render_template('forms/edit_venue.html', form=form, venue=venue)

@app.route('/venues/<int:venue_id>/edit', methods=['POST'])
def edit_venue_submission(venue_id):
  error = False
  name = request.form['name']
  city = request.form['city']
  state = request.form['state']
  address = request.form['address']
  phone = request.form['phone']
  genres = request.form.getlist('genres')
  image_link = request.form['image_link']
  facebook_link = request.form['facebook_link']
  website = request.form['website']
  seeking_talent = True if 'seeking_talent' in request.form else False
  seeking_description = request.form['seeking_description']

  try:
    venue = Venue.query.get(venue_id)
    venue.name = name
    venue.city = city
    venue.state = state
    venue.address = address
    venue.phone = phone
    venue.genres = genres
    venue.image_link = image_link
    venue.facebook_link = facebook_link
    venue.website = website
    venue.seeking_talent = seeking_talent
    venue.seeking_description = seeking_description

    db.session.commit()
  except Exception:
      error = True
      db.session.rollback()
      print(sys.exc_info())
  finally:
      db.session.close()
  if error:
    #abort(400)
    flash('Venue ' + name + ' could not be updated')
  if not error:
    flash('Venue ' + name + ' has been updated')

  return redirect(url_for('show_venue', venue_id=venue_id))

#  Create Artist
#  ----------------------------------------------------------------

@app.route('/artists/create', methods=['GET'])
def create_artist_form():
  form = ArtistForm()
  return render_template('forms/new_artist.html', form=form)

@app.route('/artists/create', methods=['POST'])
def create_artist_submission():
  error = False
  form = ArtistForm()
  if not form.validate():
    for fieldName, errorMessages in form.errors.items():
      #show_form_errors(fieldName,errorMessages)
      return redirect(url_for('create_artist_form'))
  name = request.form['name']
  city = request.form['city']
  state = request.form['state']
  phone = request.form['phone']
  genres = request.form.getlist('genres')
  image_link = request.form['image_link']
  facebook_link = request.form['facebook_link']
  website = request.form['website']
  seeking_venue = True if 'seeking_venue' in request.form else False
  seeking_description = request.form['seeking_description']

  try:
    artist = Artist(name=name, city=city, state=state, phone=phone,
        genres=genres, image_link=image_link, facebook_link=facebook_link,
        website=website, seeking_venue=seeking_venue,
        seeking_description=seeking_description)
    db.session.add(artist)
    db.session.commit()

  except Exception:
    error = True
    db.session.rollbask()
    print(sys.exc_info)
  finally:
    db.session.close()

  if error:
    # abort(400)
    flash('Artist ' + name + ' could not be listed')
  if not error:
    flash('Artist ' + name + ' has been updated')

  return render_template('pages/home.html')
#  Shows
#  ----------------------------------------------------------------

@app.route('/shows')
def shows():
  data = []
  shows = db.session.query(Venue.name, Artist.name, Artist.image_link,
                          Show.venue_id, Show.artist_id, Show.start_time).filter(Venue.id == Show.venue_id,
                          Artist.id == Show.artist_id)

  for show in shows:
    data.append({'venue_name': show[0], 'artist_name': show[1], 'artist_image' : show[2],
                  'venue_id': show[3], 'artist_id':show[4], 'start_time': str(show[5])})

  return render_template('pages/shows.html', shows = data)

@app.route('/shows/create')
def create_shows():
  # renders form. do not touch.
  form = ShowForm()
  return render_template('forms/new_show.html', form=form)

@app.route('/shows/create', methods=['POST'])
def create_show_submission():
  error = False
  artist_id = request.form['artist_id']
  venue_id = request.form['venue_id']
  start_time = request.form['start_time']

  try:
    show = Show(
      artist_id=artist_id,
      venue_id=venue_id,
      start_time=start_time,
    )
    db.session.add(show)
    db.session.commit()

  except Exception:
    error = True
    db.session.rollback()
    print(sys.exc_info())

  finally:
    db.session.close()

  if error:
    #abort(400)
    flash('Show could not be listed.')

  if not error:
      flash('Show was successfully listed!')

  return render_template('pages/home.html')

@app.errorhandler(404)
def not_found_error(error):
    return render_template('errors/404.html'), 404

@app.errorhandler(500)
def server_error(error):
    return render_template('errors/500.html'), 500

if not app.debug:
    file_handler = FileHandler('error.log')
    file_handler.setFormatter(
        Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]')
    )
    app.logger.setLevel(logging.INFO)
    file_handler.setLevel(logging.INFO)
    app.logger.addHandler(file_handler)
    app.logger.info('errors')

#----------------------------------------------------------------------------#
# Launch.
#----------------------------------------------------------------------------#

# Default port:
if __name__ == '__main__':
    app.run()

# Or specify port manually:
'''
if __name__ == '__main__':
    port = int(os.environ.get('PORT', 5000))
    app.run(host='0.0.0.0', port=port)
'''


# USAGE
# python /home/nmorales/cxgn/DroneImageScripts/ImageProcess/CalculatePhenotypeAutoEncoderVegetationIndices.py

# import the necessary packages
import sys
import argparse
import imutils
import cv2
import numpy as np
import math
from matplotlib import pyplot as plt
import statistics
from collections import defaultdict
import csv
import pandas as pd
from sklearn.model_selection import train_test_split
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.layers import Conv2DTranspose
from tensorflow.keras.layers import LeakyReLU
from tensorflow.keras.layers import Activation
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Reshape
from tensorflow.keras.optimizers import Adam
from tensorflow.keras import Input
from tensorflow.keras import Model
from tensorflow.keras import backend as K
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-l", "--log_file_path", required=False, help="file path to write log to. useful for using from the web interface")
ap.add_argument("-k", "--input_training_image_file", required=True, help="file path with stock_id and image paths for training autoencoder")
ap.add_argument("-i", "--input_image_file", required=True, help="file path with stock_id and image paths")
ap.add_argument("-r", "--outfile_path", required=True, help="file path where results will be saved")
ap.add_argument("-j", "--autoencoder_model_type", required=True, help="type of autoencoder model")
ap.add_argument("-o", "--output_encoded_images_file", required=True, help="file path with file paths for encoded stock images")
args = vars(ap.parse_args())

log_file_path = args["log_file_path"]
input_training_images_file = args["input_training_image_file"]
input_images_file = args["input_image_file"]
autoencoder_model_type = args["autoencoder_model_type"]
results_outfile = args["outfile_path"]
output_encoded_images_file = args["output_encoded_images_file"]

if sys.version_info[0] < 3:
    raise Exception("Must use Python3. Use python3 in your command line.")

if log_file_path is not None:
    sys.stderr = open(log_file_path, 'a')

def eprint(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)

result_file_lines = [
    ['stock_id', 'ndvi', 'ndre']
]

image_size = 96

def get_imagedatagenerator():
    datagen = ImageDataGenerator(
        featurewise_center=True,
        featurewise_std_normalization=True,
        #rotation_range=20,
        #width_shift_range=0.05,
        #height_shift_range=0.05,
        #horizontal_flip=True,
        # vertical_flip=True,
        #brightness_range=[0.8,1.2]
    )
    return datagen

def build_autoencoder(width, height, depth, filters=(32, 64), latentDim=16):
    inputShape = (height, width, depth)
    chanDim = -1

    # define the input to the encoder
    inputs = Input(shape=inputShape)
    x = inputs

    # loop over the number of filters
    for f in filters:
        # apply a CONV => RELU => BN operation
        x = Conv2D(f, (3, 3), strides=2, padding="same")(x)
        x = LeakyReLU(alpha=0.2)(x)
        x = BatchNormalization(axis=chanDim)(x)

    # flatten the network and then construct our latent vector
    volumeSize = K.int_shape(x)
    x = Flatten()(x)
    latent = Dense(latentDim)(x)

    # build the encoder model
    encoder = Model(inputs, latent, name="encoder")

    # start building the decoder model which will accept the
    # output of the encoder as its inputs
    latentInputs = Input(shape=(latentDim,))
    x = Dense(np.prod(volumeSize[1:]))(latentInputs)
    x = Reshape((volumeSize[1], volumeSize[2], volumeSize[3]))(x)

    # loop over our number of filters again, but this time in
    # reverse order
    for f in filters[::-1]:
        # apply a CONV_TRANSPOSE => RELU => BN operation
        x = Conv2DTranspose(f, (3, 3), strides=2, padding="same")(x)
        x = LeakyReLU(alpha=0.2)(x)
        x = BatchNormalization(axis=chanDim)(x)

    # apply a single CONV_TRANSPOSE layer used to recover the
    # original depth of the image
    x = Conv2DTranspose(depth, (3, 3), padding="same")(x)
    outputs = Activation("sigmoid")(x)

    # build the decoder model
    decoder = Model(latentInputs, outputs, name="decoder")

    # our autoencoder is the encoder + decoder
    autoencoder = Model(inputs, decoder(encoder(inputs)), name="autoencoder")

    # return a 3-tuple of the encoder, decoder, and autoencoder
    return (encoder, decoder, autoencoder)

input_training_image_file_data = pd.read_csv(input_training_images_file, sep="\t", header=0)
input_image_file_data = pd.read_csv(input_images_file, sep="\t", header=0)
output_encoded_images_file_data = pd.read_csv(output_encoded_images_file, sep="\t", header=0)

output_ndvi_images = []
output_ndre_images = []
for index, row in output_encoded_images_file_data.iterrows():
    stock_id = row[0]
    output_ndvi_image = row[1]
    output_ndre_image = row[2]
    output_ndvi_images.append(output_ndvi_image)
    output_ndre_images.append(output_ndre_image)

stock_ids = []
nir_images_data = []
stock_nir_images_data = []
red_images_data = []
stock_red_images_data = []
rededge_images_data = []
stock_rededge_images_data = []
for index, row in input_image_file_data.iterrows():
    stock_id = row[0]
    red_images = row[1]
    rededge_images = row[2]
    nir_images = row[3]

    stock_ids.append(stock_id)
    red_images_array = red_images.split(',')
    rededge_images_array = rededge_images.split(',')
    nir_images_array = nir_images.split(',')

    stock_nir_images_array = []
    for nir_img in nir_images_array:
        image = cv2.imread(nir_img, cv2.IMREAD_UNCHANGED)
        image = cv2.resize(image, (image_size, image_size)) / 255.0

        if (len(image.shape) == 2):
            empty_mat = np.ones(image.shape, dtype=image.dtype) * 0
            image = cv2.merge((image, empty_mat, empty_mat))

        nir_images_data.append(image)
        stock_nir_images_array.append(image)
    stock_nir_images_data.append(np.array(stock_nir_images_array))

    stock_red_images_array = []
    for red_img in red_images_array:
        image = cv2.imread(red_img, cv2.IMREAD_UNCHANGED)
        image = cv2.resize(image, (image_size, image_size)) / 255.0

        if (len(image.shape) == 2):
            empty_mat = np.ones(image.shape, dtype=image.dtype) * 0
            image = cv2.merge((image, empty_mat, empty_mat))

        red_images_data.append(image)
        stock_red_images_array.append(image)
    stock_red_images_data.append(np.array(stock_red_images_array))

    stock_rededge_images_array = []
    for rededge_img in rededge_images_array:
        image = cv2.imread(rededge_img, cv2.IMREAD_UNCHANGED)
        image = cv2.resize(image, (image_size, image_size)) / 255.0

        if (len(image.shape) == 2):
            empty_mat = np.ones(image.shape, dtype=image.dtype) * 0
            image = cv2.merge((image, empty_mat, empty_mat))

        rededge_images_data.append(image)
        stock_rededge_images_array.append(image)
    stock_rededge_images_data.append(np.array(stock_rededge_images_array))

nir_images_data = np.array(nir_images_data)
red_images_data = np.array(red_images_data)
rededge_images_data = np.array(rededge_images_data)
stock_nir_images_data = np.array(stock_nir_images_data)
stock_red_images_data = np.array(stock_red_images_data)
stock_rededge_images_data = np.array(stock_rededge_images_data)

training_stock_ids = []
training_nir_images_data = []
training_stock_nir_images_data = []
training_red_images_data = []
training_stock_red_images_data = []
training_rededge_images_data = []
training_stock_rededge_images_data = []
for index, row in input_training_image_file_data.iterrows():
    stock_id = row[0]
    red_images = row[1]
    rededge_images = row[2]
    nir_images = row[3]

    if pd.isnull(red_images):
        continue

    training_stock_ids.append(stock_id)
    training_red_images_array = red_images.split(',')
    training_rededge_images_array = rededge_images.split(',')
    training_nir_images_array = nir_images.split(',')

    training_stock_nir_images_array = []
    for nir_img in training_nir_images_array:
        image = cv2.imread(nir_img, cv2.IMREAD_UNCHANGED)
        image = cv2.resize(image, (image_size, image_size)) / 255.0

        if (len(image.shape) == 2):
            empty_mat = np.ones(image.shape, dtype=image.dtype) * 0
            image = cv2.merge((image, empty_mat, empty_mat))

        training_nir_images_data.append(image)
        training_stock_nir_images_array.append(image)
    training_stock_nir_images_data.append(np.array(training_stock_nir_images_array))

    training_stock_red_images_array = []
    for red_img in training_red_images_array:
        image = cv2.imread(red_img, cv2.IMREAD_UNCHANGED)
        image = cv2.resize(image, (image_size, image_size)) / 255.0

        if (len(image.shape) == 2):
            empty_mat = np.ones(image.shape, dtype=image.dtype) * 0
            image = cv2.merge((image, empty_mat, empty_mat))

        training_red_images_data.append(image)
        training_stock_red_images_array.append(image)
    training_stock_red_images_data.append(np.array(training_stock_red_images_array))

    training_stock_rededge_images_array = []
    for rededge_img in training_rededge_images_array:
        image = cv2.imread(rededge_img, cv2.IMREAD_UNCHANGED)
        image = cv2.resize(image, (image_size, image_size)) / 255.0

        if (len(image.shape) == 2):
            empty_mat = np.ones(image.shape, dtype=image.dtype) * 0
            image = cv2.merge((image, empty_mat, empty_mat))

        training_rededge_images_data.append(image)
        training_stock_rededge_images_array.append(image)
    training_stock_rededge_images_data.append(np.array(training_stock_rededge_images_array))

training_nir_images_data = np.array(training_nir_images_data)
training_red_images_data = np.array(training_red_images_data)
training_rededge_images_data = np.array(training_rededge_images_data)
training_stock_nir_images_data = np.array(training_stock_nir_images_data)
training_stock_red_images_data = np.array(training_stock_red_images_data)
training_stock_rededge_images_data = np.array(training_stock_rededge_images_data)

nir_datagen = get_imagedatagenerator()
nir_datagen.fit(training_nir_images_data)
training_nir_images_data = nir_datagen.standardize(training_nir_images_data)
nir_images_data = nir_datagen.standardize(nir_images_data)

red_datagen = get_imagedatagenerator()
red_datagen.fit(training_red_images_data)
training_red_images_data = red_datagen.standardize(training_red_images_data)
red_images_data = red_datagen.standardize(red_images_data)

rededge_datagen = get_imagedatagenerator()
rededge_datagen.fit(training_rededge_images_data)
training_rededge_images_data = rededge_datagen.standardize(training_rededge_images_data)
rededge_images_data = rededge_datagen.standardize(rededge_images_data)

(nir_encoder, nir_decoder, nir_autoencoder) = build_autoencoder(image_size, image_size, 3)
(red_encoder, red_decoder, red_autoencoder) = build_autoencoder(image_size, image_size, 3)
(rededge_encoder, rededge_decoder, rededge_autoencoder) = build_autoencoder(image_size, image_size, 3)

opt = Adam(lr=1e-3)
nir_autoencoder.compile(loss="mse", optimizer=opt)
red_autoencoder.compile(loss="mse", optimizer=opt)
rededge_autoencoder.compile(loss="mse", optimizer=opt)

(training_nir_train_images, training_nir_test_images, training_nir_train_images, training_nir_test_images) = train_test_split(training_nir_images_data, training_nir_images_data, test_size=0.2)
(nir_train_images, nir_test_images, nir_train_images, nir_test_images) = train_test_split(nir_images_data, nir_images_data, test_size=0.2)
(training_red_train_images, training_red_test_images, training_red_train_images, training_red_test_images) = train_test_split(training_red_images_data, training_red_images_data, test_size=0.2)
(red_train_images, red_test_images, red_train_images, red_test_images) = train_test_split(red_images_data, red_images_data, test_size=0.2)
(training_rededge_train_images, training_rededge_test_images, training_rededge_train_images, training_rededge_test_images) = train_test_split(training_rededge_images_data, training_rededge_images_data, test_size=0.2)
(rededge_train_images, rededge_test_images, rededge_train_images, rededge_test_images) = train_test_split(rededge_images_data, rededge_images_data, test_size=0.2)

#checkpoint = ModelCheckpoint(filepath=output_autoencoder_model_file_path, monitor='loss', verbose=1, save_best_only=True, mode='min', save_frequency=1, save_weights_only=False)
#callbacks_list = [checkpoint]

H_nir = nir_autoencoder.fit(
    training_nir_train_images, training_nir_train_images,
    validation_data=(training_nir_test_images, training_nir_test_images),
    epochs=25,
    batch_size=32,
    #callbacks=callbacks_list
)
#nir_decoded = nir_autoencoder.predict(nir_images_data)

H_red = red_autoencoder.fit(
    training_red_train_images, training_red_train_images,
    validation_data=(training_red_test_images, training_red_test_images),
    epochs=25,
    batch_size=32,
    #callbacks=callbacks_list
)
#red_decoded = red_autoencoder.predict(red_images_data)

H_rededge = rededge_autoencoder.fit(
    training_rededge_train_images, training_rededge_train_images,
    validation_data=(training_rededge_test_images, training_rededge_test_images),
    epochs=25,
    batch_size=32,
    #callbacks=callbacks_list
)
#rededge_decoded = rededge_autoencoder.predict(rededge_images_data)

stock_counter = 0
for stock_id in stock_ids:
    nir_images = stock_nir_images_data[stock_counter]
    nir_decoded = nir_autoencoder.predict(nir_images)

    red_images = stock_red_images_data[stock_counter]
    red_decoded = red_autoencoder.predict(red_images)

    rededge_images = stock_rededge_images_data[stock_counter]
    rededge_decoded = rededge_autoencoder.predict(rededge_images)

    ndvi = np.divide(nir_decoded[0][:,:,0] - red_decoded[0][:,:,0], nir_decoded[0][:,:,0] + red_decoded[0][:,:,0])
    ndvi[np.isnan(ndvi)] = 0
    ndvi = ndvi * 255
    ndvi = ndvi.astype(np.uint8)

    ndre = np.divide(nir_decoded[0][:,:,0] - rededge_decoded[0][:,:,0], nir_decoded[0][:,:,0] + rededge_decoded[0][:,:,0])
    ndre[np.isnan(ndre)] = 0
    ndre = ndre * 255
    ndre = ndre.astype(np.uint8)

    cv2.imwrite(output_ndvi_images[stock_counter], ndvi)
    cv2.imwrite(output_ndre_images[stock_counter], ndre)

    stock_counter += 1

    ndvi_list = []
    ndre_list = []

    img_counter = 0
    for image in nir_images:
        nir_img = nir_decoded[img_counter][:,:,0]
        red_img = red_decoded[img_counter][:,:,0]
        rededge_img = rededge_decoded[img_counter][:,:,0]

        img_counter += 1

        nir_mean_pixel = nir_img.mean(axis=0).mean()
        red_mean_pixel = red_img.mean(axis=0).mean()
        rededge_mean_pixel = rededge_img.mean(axis=0).mean()

        nir_mean_pixel = nir_mean_pixel / 255
        red_mean_pixel = red_mean_pixel / 255
        rededge_mean_pixel = rededge_mean_pixel / 255

        ndvi_list.append((nir_mean_pixel - red_mean_pixel)/(nir_mean_pixel + red_mean_pixel))
        ndre_list.append((nir_mean_pixel - rededge_mean_pixel)/(nir_mean_pixel + rededge_mean_pixel))

    result_file_lines.append([
        stock_id,
        statistics.mean(ndvi_list),
        statistics.mean(ndre_list)
    ])

with open(results_outfile, 'w') as writeFile:
    writer = csv.writer(writeFile)
    writer.writerows(result_file_lines)

writeFile.close()


import pygame as pg
from pygame.locals import *
from column import Column
from score import Score
from menu import menuEnum, pauseMenu, gameoverMenu
import random

# The main game class
class Game:

    def __init__(self, gameConfig, lvlConfig):

        # Save the config
        self.screen = pg.display.get_surface()
        self.gameConfig = gameConfig
        self.lvlConfig = lvlConfig

        # Variables
        self.game_speed = gameConfig.speed
        self.generation_frequency = lvlConfig.gen_frequency
        self.active = gameConfig.default_active

        # Columns
        self.col_group = pg.sprite.Group()
        self.input_group = pg.sprite.Group()

        # The score
        self.score = pg.sprite.GroupSingle(Score(
            lvlConfig.lvl,
            lvlConfig.columns,
            pg.font.Font(self.gameConfig.font_path, 30),
            self.gameConfig.score_rect
        ))

        # Events
        self.GENERATE = USEREVENT + 1 # Generate new equation event
        self.SPEEDUP = USEREVENT + 2  # Increase the game speed event

        self.paused = False

        self.__setGraphics()

    # Create and set columns to their correct positions
    def __setGraphics(self):
        self.columns_rect = pg.Rect(
            self.gameConfig.board_left,
            self.gameConfig.board_top,
            self.gameConfig.col_width * self.lvlConfig.columns,
            self.gameConfig.col_height + self.gameConfig.input_height
        )
        for i in range(self.lvlConfig.columns):
            col_rect = pg.Rect(
                self.gameConfig.board_left + i * self.gameConfig.col_width,
                self.gameConfig.board_top,
                self.gameConfig.col_width,
                self.gameConfig.col_height)
            self.col_group.add(Column(
                i,
                self.gameConfig,
                self.lvlConfig,
                col_rect,
                self.input_group))
        self.columns = self.col_group.sprites() # Alias
        self.active_col = self.columns[self.gameConfig.default_active]

    def reset(self):
        self.score.sprite.reset()
        for col in self.columns:
            col.reset()
        self.game_speed = self.gameConfig.speed
        self.generation_frequency = self.lvlConfig.gen_frequency

        self.active_col.deactivate()
        self.active_col = self.columns[self.gameConfig.default_active]
        self.active_col.activate()

        pg.time.set_timer(self.GENERATE, self.generation_frequency)
        pg.time.set_timer(self.SPEEDUP, self.lvlConfig.inc_frequency)

        pg.event.clear()

        self.paused = False

    def run(self):

        clock = pg.time.Clock()
        self.active_col = self.columns[self.gameConfig.default_active]
        self.active_col.activate()

        pg.time.set_timer(self.GENERATE, self.lvlConfig.gen_frequency)
        pg.time.set_timer(self.SPEEDUP, self.lvlConfig.inc_frequency)

        # Create menus
        pauseM = pauseMenu(self.screen.get_width(), self.screen.get_height())
        pauseM.disable()

        gameoverM = gameoverMenu(self.columns_rect.width, self.columns_rect.height)
        gameoverM.set_absolute_position(self.columns_rect.left, self.columns_rect.top)
        gameoverM.disable()

        self.paused = False # paused iff either pause or gameover menu is active

        self.screen.fill((0, 0, 0))
        pg.display.flip()
        while 1:
            events = pg.event.get()
            for event in events:
                if event.type == QUIT:
                    return

                # Check game-related events iff not self.paused
                if not self.paused:

                    if event.type == self.GENERATE:
                        # Generate new equation for a random column
                        self.columns[random.randrange(0, self.lvlConfig.columns)].generate_eq()

                    elif event.type == self.SPEEDUP:
                        self.game_speed *= self.gameConfig.increment
                        self.generation_frequency = self.generation_frequency // self.gameConfig.increment
                        pg.time.set_timer(self.GENERATE, int(self.generation_frequency))

                    elif event.type == KEYDOWN:

                        k = event.key
                        # Change active column to one to the left or the right
                        if k in [K_LEFT, K_RIGHT]:
                            self.active_col.deactivate()
                            if k == K_LEFT:
                                new_active = self.active_col.index - 1
                            else:
                                new_active = self.active_col.index + 1
                            new_active %= self.lvlConfig.columns
                            self.active_col = self.columns[new_active]
                            self.active_col.activate()

                        # Get input into the active column's input field
                        elif k in [K_BACKSPACE, K_MINUS] or k in range(K_0, K_9 + 1):
                            self.active_col.get_input(event.key)

                        # Check the input entered into the active field
                        # And update the score
                        elif k == K_RETURN:
                            self.active_col.check(self.game_speed, self.score)

                        # Pause the game
                        elif k == K_ESCAPE:
                            self.paused = True
                            pauseM.enable()

            # Display pause menu
            if pauseM.is_enabled():
                pauseM.draw(self.screen)
                pauseM.update(events)
                pg.display.flip()

                if pauseM.state == menuEnum.BACK_TO_MENU:
                    pauseM.disable()
                    return
                elif pauseM.state == menuEnum.RESUME:
                    pauseM.state = menuEnum.OPEN
                    pauseM.disable()
                    self.screen.fill((0, 0, 0))
                    pg.display.flip()

            # Display gameover menu
            elif gameoverM.is_enabled():
                gameoverM.draw(self.screen)
                gameoverM.update(events)
                pg.display.flip()

                if gameoverM.state == menuEnum.BACK_TO_MENU:
                    gameoverM.disable()
                    return
                elif gameoverM.state == menuEnum.RESTART:
                    gameoverM.state = menuEnum.OPEN
                    gameoverM.disable()
                    self.reset()
                    self.screen.fill((0, 0, 0))
                    pg.display.flip()

            # Else display and update the game
            else:
                self.screen.fill((0, 0, 0))

                # Draw the game elements
                self.col_group.draw(self.screen)
                self.input_group.draw(self.screen)
                self.score.draw(self.screen)

                # Check for gameover when updating columns
                # (whether an equation hits the bottom)
                self.paused = False
                for col in self.columns:
                    gameover = col.update(self.game_speed / self.gameConfig.fps)
                    if gameover:
                        gameoverM.enable()
                        self.paused = True
                        break

                # Update only parts of the screen containing
                # the game elements
                pg.display.update([self.columns_rect, self.gameConfig.score_rect])

            clock.tick(self.gameConfig.fps)


import pygame
import random
import sys

pygame.init()

width=800
height=800
play_width=600
play_height=600
block_size=30

top_left_x=(width-play_width)//2
top_left_y=(height-play_height)//2

dug={(-1,-1)}

imageb=pygame.image.load('Minesweeper/Image/bomb.png')
imagef=pygame.image.load('Minesweeper/Image/flag.png')
image1=pygame.image.load('Minesweeper/Image/one.png')
image2=pygame.image.load('Minesweeper/Image/two.png')
image3=pygame.image.load('Minesweeper/Image/three.png')
image4=pygame.image.load('Minesweeper/Image/four.png')
image5=pygame.image.load('Minesweeper/Image/five.png')
image6=pygame.image.load('Minesweeper/Image/six.png')
image7=pygame.image.load('Minesweeper/Image/seven.png')
image8=pygame.image.load('Minesweeper/Image/eight.png')
trophy=pygame.image.load('Minesweeper/Image/trophy.png')
skull=pygame.image.load('Minesweeper/Image/losing.png')
background=pygame.image.load('Minesweeper/Image/background.jpg')
back=pygame.image.load('Minesweeper/Image/back.jpg')
replay=pygame.image.load('Minesweeper/Image/replay.png')
replay1=pygame.image.load('Minesweeper/Image/replay1.png')
caption=pygame.image.load('Minesweeper/Image/icon.png')

icon=[None,image1,image2,image3,image4,image5,image6,image7,image8,imageb,imagef]

def print_board(screen):
    for i in range(play_width//block_size+1):
        pygame.draw.line(screen,(0,0,0),(top_left_x+i*block_size,top_left_y),\
            (top_left_x+i*block_size,top_left_y+play_height),3)
    for j in range(play_height//block_size+1):
        pygame.draw.line(screen,(0,0,0),(top_left_x,top_left_y+j*block_size),\
        (top_left_x+play_width,top_left_y+j*block_size),3)

def print_grid(screen,grid,bomb=True):
    for i in range(play_width//block_size):
        for j in range(play_height//block_size):
            pygame.draw.rect(screen,(255,255,255),(top_left_x+i*block_size,\
            top_left_y+j*block_size,block_size,block_size))
            if grid[i][j][1]!=None and grid[i][j][0]==None:
                if grid[i][j][1]==0:
                    pygame.draw.rect(screen,(105,105,105),(top_left_x+i*block_size,\
                top_left_y+j*block_size,block_size,block_size))
                else:
                    if bomb:
                        if grid[i][j][1]==9:
                            continue
                    screen.blit(icon[grid[i][j][1]],(top_left_x+i*block_size+2,\
                    top_left_y+j*block_size+4))
            if grid[i][j][0]==1:
                screen.blit(icon[10],(top_left_x+i*block_size+2,\
                    top_left_y+j*block_size+4))
    print_board(screen)

def print_text(surface,string,x,y,size,color):
    font=pygame.font.SysFont('comicsans',size)
    render=font.render(string,True,color)
    font.set_bold(True)
    surface.blit(render,(x,y))

def win_condition(grid):
    for i in range(play_width//block_size):
        for j in range(play_height//block_size):
            if grid[i][j][1]==None:
                return True
    return False

def place_mines(nums,grid):
    mines=0
    while mines<nums:
        x,y=random.randint(0,play_width//block_size-1),random.randint(0,play_height//block_size-1)
        if grid[x][y][1]!=9:
            grid[x][y][1]=9
            mines+=1

def number_mines(grid,x,y):
    if grid[x][y][1]==9:
        return 9
    number_mines=0
    for i in range(max(0,x-1),min(play_width//block_size,x+2)):
        for j in range(max(0,y-1),min(play_height//block_size,y+2)):
            if grid[i][j][1]==9:
                number_mines+=1
    return number_mines

def dig(grid,x,y):
    global dug
    dug.add((x,y))
    if number_mines(grid,x,y)!=0:
        grid[x][y][1]=number_mines(grid,x,y)
        return True
    elif number_mines(grid,x,y)==0:
        grid[x][y][1]=0
    for r in range(max(0,x-1),min(play_width//block_size,x+2)):
        for c in range(max(0,y-1),min(play_height//block_size,y+2)):
            if (r,c) in dug:
                continue
            dig(grid,r,c)

def main(screen):
    global dug
    final_grid=[[[None,None] for _ in range(play_width//block_size)]\
    for _ in range(play_height//block_size)]
    place_mines(20,final_grid)
    running=True
    screen.blit(background,(0,0))
    screen.blit(replay,(20,20))
    while running:
        x,y=pygame.mouse.get_pos()
        if 20<x<84 and 20<y<84:
            screen.blit(replay1,(20,20))
        else:
            screen.blit(replay,(20,20))
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                running=False
            if win_condition(final_grid) or running:
                if event.type==pygame.MOUSEBUTTONDOWN:
                    if 20<x<84 and 20<y<84:
                        final_grid=[[[None,None] for _ in range(play_width//block_size)]\
                        for _ in range(play_height//block_size)]
                        place_mines(20,final_grid)
                        dug={(-1,-1)}
                        running=True
                    if top_left_x<x<top_left_x+play_width and\
                        top_left_y<y<top_left_y+play_height:
                        col=(x-top_left_x)//block_size
                        row=(y-top_left_y)//block_size
                        if event.button==1:
                            if final_grid[col][row][1]==9:
                                print_grid(screen,final_grid,bomb=False)
                                running=False
                                break
                            if final_grid[col][row][0]==None:
                                dig(final_grid,col,row)
                        if event.button==3:
                            if final_grid[col][row][1] in (None,9) and final_grid[col][row][0]==None:
                                final_grid[col][row][0]=1
                            elif final_grid[col][row][0]==1:
                                final_grid[col][row][0]=None
                print_grid(screen,final_grid)
        if not running:
            dug={(-1,-1)}
            print_grid(screen,final_grid,bomb=False)
            screen.blit(skull,(width/2-256/2,height/2-256))
        if not win_condition(final_grid):
            running=False
            dug={(-1,-1)}
            print_grid(screen,final_grid,bomb=False)
            screen.blit(trophy,(width/2-256/2,height/2-256))
        pygame.display.update()

    print_text(screen,'PRESS TO PLAY',150,height//2,100,(255,0,0))
    print_text(screen,'AGAIN',300,height//2+100,100,(255,0,0))

def main_menu():
    screen=pygame.display.set_mode((width,height))
    pygame.display.set_caption('Minesweeper')
    pygame.display.set_icon(caption)
    screen.blit(back,(0,0))
    print_text(screen,'PRESS TO PLAY',150,height//2,100,(255,255,255))
    run=True
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run=False
            if event.type == pygame.KEYDOWN:
                main(screen)
        pygame.display.update()

if __name__=='__main__':
    main_menu()


#!/usr/bin/env python3

"""Log Analysis Project"""

import psycopg2

"""Code"""
"""***********************************"""

"""Connects to the news database and returns a database cursor."""

def connect_to_database():
    try:
        database = psycopg2.connect("dbname=news")
        dbCursor = database.cursor()
        return dbCursor
    except DatabaseError:
        print("Unable to connect to the database")

"""Query and print: Most popular articles."""

def popularArticles(dbCursor):
    query = """
            select articles.title, count(*)from  log, articles
            where  log.path = '/article/' || articles.slug
            group by articles.title
            order by count(*) desc
            limit 3;
    """
    dbCursor.execute(query)
    x = dbCursor.fetchall()

    print()
    print()
    print('3 Most Popular Articles')
    print('**************************************************')

    for result in x:
        print('"{articleName}" - {viewCount} views'.format
              (articleName=result[0], viewCount=result[1]))

    print()

    return

"""Query and print: most popular authors."""

def popularAuthors(dbCursor):
    query = """
            select authors.name, count(*) from   log, articles, authors
            where  log.path = '/article/' || articles.slug
            and articles.author = authors.id
            group by authors.name
            order by count(*) desc;
    """
    dbCursor.execute(query)
    y = dbCursor.fetchall()

    print()
    print()
    print('Most Popular Authors')
    print('**************************************************')

    for result in y:
        print('{authorName} - {viewCount} views'.format
              (authorName=result[0], viewCount=result[1]))

    print()

    return

"""Query and print:Days error rate is greater than 1%."""

def daysGreaterThan1pError(dbCursor):
    query = """
            create view requests
            as select time::date as date, count(*) from log
            group by time::date
            order by time::date;

            create view errors
            as select time::date as date, count(*) from log
            where status != '200 OK'
            group by time::date
            order by time::date;

            create view requests_errors as
            select requests.date,
            errors.count::float / requests.count::float * 100
            as errorrate from requests, errors
            where requests.date = errors.date;

            select * from requests_errors where errorrate > 1;

    """
    dbCursor.execute(query)
    z = dbCursor.fetchall()

    print()
    print()
    print('Dates Error Rate is Greater than 1%')
    print('**************************************************')

    for result in z:
        print('{date:} - {errorRate:}% errors'.format
              (date=result[0], errorRate=result[1]))

    print()
    print()

    return

"""Call functions from code"""
"""***********************************"""

if __name__ == "__main__":
    dbCursor = connect_to_database()
    if dbCursor:
        popularArticles(dbCursor)
        popularAuthors(dbCursor)
        daysGreaterThan1pError(dbCursor)
        dbCursor.close()


import numpy as np
from scipy import signal, misc
import matplotlib.pyplot as plt
from scipy import ndimage
from mpl_toolkits.mplot3d import Axes3D

sigma = 30

x=np.arange(-128,127,1.0)
y=np.arange(-128,127,1.0)
X,Y=np.meshgrid(x,y)

s=1/(np.pi*pow(sigma,4))
a=-(pow(X,2)+pow(Y,2))/(2*pow(sigma,2))
g=-s*(1+a)*np.exp(a)

#a
plt.imshow(g)
plt.gray()
plt.title('LoG(x,y)')
plt.axis('off')
plt.show()

#b
fig=plt.figure()
ax=Axes3D(fig)
ax.plot_surface(X,Y,g)
plt.show()

#c
#9*9 LoG 필터
def LoG_FIlter(sigma,Filter_Size):
    g = np.zeros(shape=(Filter_Size,Filter_Size), dtype=np.float)
    for y in range(-4,5):
        for x in range(-4,5):
            s=1/(np.pi*pow(sigma,4))
            a=-(pow(x,2)+pow(y,2))/(2*pow(sigma,2))
            p=-s*(1+a)*np.exp(a)
            g[y+4,x+4]=p
    return g
Filter_Size = 9
print(LoG_FIlter(0.8,Filter_Size))

#d

def im_filtering(im, Filter, FilterSize):
    row, col = im.shape
    padding=int(FilterSize/2)

    Image_Buffer = np.zeros(shape=(row+2*padding,col+2*padding), dtype=np.uint8)
    Image_Buffer[padding:row+padding, padding:col+padding] = im[:,:]
    Image_New = np.zeros(shape=(row,col), dtype=np.uint8)
    for y in range(padding,row+padding):
        for x in range(padding,col+padding):
            buff = Image_Buffer[y-padding:y+padding+1,x-padding:x+padding+1]
            pixel = np.sum(buff * Filter)
            pixel = np.uint8(np.where(pixel>255,255,np.where(pixel<0,0,pixel)))
            Image_New[y-padding,x-padding] = pixel
    return Image_New

lena = misc.imread('image/lena_256.bmp')
filtering_img = im_filtering(lena, LoG_FIlter(0.8,Filter_Size), Filter_Size)

# 이미지 출력 함수
def image_print(img, title, print_num, current_num):
    plt.subplot(print_num[0],print_num[1],current_num)
    plt.title(title)
    plt.gray()
    plt.imshow(img)
    plt.axis('off')

print_num = [1,2]
image_print(lena, "InputImage",  print_num, 1)
image_print(filtering_img, "Log Filtering Image",  print_num, 2)
plt.show()


#coding:utf8
"""
pip install makedown
pip install pillow
pip install requests

markdown document: https://python-markdown.github.io/
"""
import os,sys
import re
import markdown
from PIL import Image
import io
import base64
import getopt
import requests

def usage():
    print(f"using: python {sys.argv[0]} [options] [arg]")
    print(" -h, --help            : 显示使用帮助")
    print(" -t, --title           : 生成的HTML文档的title")
    print(" -o, --output          : 生成的HTML文档的保存位置")
    print("     --theme           : 生成的HTML文档额外的CSS样式")
    print("     --header          : 在正文之前（标签后边）加入html内容")
    print("     --footer          : 在正文之后（标签前边）加入html内容")
    print("     --image-base64    : 将图片转换成base64")
    print("     --max-index-depth : 将h1...h9标签编排成树状索引显示在页面的右侧，设置索引的最大深度，默认为0（不启用索引）")
    print(" -D foo=bar            : 定义变量，替换markdown文档中的“%foo%”字样")
    print(" arg                   : 要转换的markdown文件")

def download_image(url):
    resp = requests.get(url)
    return io.BytesIO(resp.content)

def image_to_base64(byteio):
    with Image.open(byteio) as image:
        image.verify()

    mime = Image.MIME[image.format]
    b64data = str(base64.b64encode(byteio.getvalue()), encoding='utf-8')

    return f"data:{mime};base64,{b64data}"

SCRIPT_DIR = os.path.split(os.path.realpath(__file__))[0]
OPTIONS = {
    "variables": {},
    "title": "",
    "output": "",
    "theme": f"{SCRIPT_DIR}/theme.css",
    "enable_image_base64": False,
    "header": "",
    "footer": "",
    "max_index_depth": 0,
}

opts, args = getopt.getopt(sys.argv[1:], "hD:t:o:", ["help", "title=", "output=", "theme=", "theme=", "header=", "footer=", "image-base64", "max-index-depth="])
if len(args) == 0:
    usage() ; quit()

for k,v in opts:
    if k == '-D':
        var = v.split("=", 1)
        OPTIONS['variables'][var[0]] = var[1]
    elif k == '--title' or k == '-t':
        OPTIONS['title'] = v
    elif k == '--output' or k == '-o':
        OPTIONS['output'] = v
    elif k == '--theme':
        OPTIONS['theme'] = v
    elif k == '--header':
        OPTIONS['header'] = v
    elif k == '--footer':
        OPTIONS['footer'] = v
    elif k == '--image-base64':
        OPTIONS['enable_image_base64'] = True
    elif k == '--max-index-depth':
        OPTIONS['max_index_depth'] = int(v);
    elif k == '--help' or k == '-h':
        usage() ; quit()

md_file = args[0]
filepath, filename = os.path.split(os.path.abspath(md_file))
name, ext = os.path.splitext(filename)

if OPTIONS['title'] == "":
    OPTIONS['title'] = name

if OPTIONS['output'] == "":
    OPTIONS['output'] = f"{name}.html"

with open(md_file, 'r') as f:
    md_text = f.read()

for varname in OPTIONS['variables']:
    md_text = md_text.replace(f"%{varname}%", OPTIONS['variables'][varname])

with open(OPTIONS['theme'], 'r') as f:
    theme_text = re.sub("\s*[\r\n]\s*", "", f.read())

index_text = None
if OPTIONS['max_index_depth'] > 0:
    with open(f"{SCRIPT_DIR}/index.js", 'r') as f:
        index_text = re.sub("\s*[\r\n]\s*", "", f.read())
        index_text = f""

header_text = ""
if OPTIONS['header']:
    with open(OPTIONS['header'], 'r') as f:
        header_text = f.read()

footer_text = ""
if OPTIONS['footer']:
    with open(OPTIONS['footer'], 'r') as f:
        footer_text = f.read()

# extensions see https://python-markdown.github.io/extensions
html = markdown.markdown(md_text, extensions=['markdown.extensions.tables'])

if OPTIONS['enable_image_base64']:
    for img in re.findall("", html):
        if img[1][:8] == 'https://' or img[1][:7] == 'http://' or img[1][:2] == '//':
            ifile = download_image(img[1])
        else:
            if os.path.isfile(img[1]):
                imgpath = img[1]
            else:
                imgpath = os.path.join(filepath, img[1])

            with open(imgpath, 'rb') as f:
                ifile = io.BytesIO(f.read())

        src = image_to_base64(ifile)

        html = re.sub(re.compile(r"()"), f"\\g<1>{src}\\g<2>", html)

output_text = f"""<!DOCTYPE html>

        <meta charset="UTF-8"/>
        <title>{OPTIONS['title']}</title>

    {index_text if not index_text is None else ""}
    {header_text}
{html}
{footer_text}

"""

with open(OPTIONS['output'], 'w') as f:
    f.write(output_text)


import os

from io import BytesIO
from PIL import Image, ImageDraw, ImageFilter, ImageFont, ImageOps
from base.classes.functions.admFunctions import fontPath

class drawLevel:
    def __init__(self):
        self.class_font_semibold = ImageFont.truetype(
            os.path.join(fontPath.pathMonserrat, 'MontserratSemiBold.ttf'), 36)

        self.class_font_bold_name = ImageFont.truetype(
            os.path.join(fontPath.pathMonserrat, 'MontserratBold.ttf'), 72)

        self.class_font_bold_id = ImageFont.truetype(
            os.path.join(fontPath.pathMonserrat, 'MontserratMedium.ttf'), 42)

        self.class_font_bold_info_sans = ImageFont.truetype(
            os.path.join(fontPath.pathOpen, 'OpenSansRegular.ttf'), 36)

        self.class_font_bold_xp = ImageFont.truetype(
            os.path.join(fontPath.pathOpen, 'OpenSansBold.ttf'), 48)

        self.class_font_bold_info = ImageFont.truetype(
            os.path.join(fontPath.pathMonserrat, 'MontserratBold.ttf'), 36)

        self.class_font_montserrat = ImageFont.truetype(
            os.path.join(fontPath.pathMonserrat, 'MontserratRegular.ttf'), 36)

        self.class_font_montserrat_bday = ImageFont.truetype(
            os.path.join(fontPath.pathMonserrat, 'MontserratMedium.ttf'), 36)

        self.class_font_bold_ori_bday = ImageFont.truetype(
            os.path.join(fontPath.pathMonserrat, 'MontserratBold.ttf'), 42)

        self.class_font_role = ImageFont.truetype(
            os.path.join(fontPath.pathMonserrat, 'MontserratBold.ttf'), 30)

        self.RadiateSansBoldCondensed = ImageFont.truetype(
            os.path.join(fontPath.pathOpen, 'OpenSansCondensedBold.ttf'), 65)

        self.class_font_montserrat_regular_nivel = ImageFont.truetype(
            os.path.join(fontPath.pathMonserrat, 'MontserratRegular.ttf'), 30)

        self.XPRadiateSansBoldCondensed = ImageFont.truetype(
            os.path.join(fontPath.pathOpen, 'RadiateSansBoldCondensed.ttf'), 85)

    @staticmethod
    def neededxp(level: int) -> int:
        return ((level * level)) + 100

    def level(
            self,
            rank: str,
            xp: str,
            xptotal: str,
            moldName: str,
            moldImg: str,
            imgxp: str,
            profile_bytes
    ) -> BytesIO:
        profile_bytes = Image.open(str(profile_bytes))
        profile_bytes_imenso = profile_bytes.copy()

        # profile_bytes = profile_bytes.resize((1270, 1270))

        bigsize = (profile_bytes.size[0] * 3, profile_bytes.size[1] * 3)
        mask = Image.new('L', bigsize, 0)
        draw = ImageDraw.Draw(mask)
        draw.ellipse((0, 0) + bigsize, fill=255)
        mask = mask.resize(profile_bytes.size, Image.Resampling.LANCZOS)
        profile_bytes.putalpha(mask)

        output = ImageOps.fit(profile_bytes, mask.size, centering=(0.5, 0.5))
        output.putalpha(mask)

        bg = Image.new('RGBA', (1270, 892), (0, 45, 62, 255))
        bg_draw = ImageDraw.Draw(bg)

        profile_bytes_imenso = profile_bytes_imenso.resize((1270, 892))
        # profile_bytes_imenso = profile_bytes_imenso.crop(
        #    (0, 420, 1280, 823+420))
        mask = Image.new("L", profile_bytes_imenso.size, 90)

        profileFundo = Image.composite(profile_bytes_imenso, bg, mask)
        profileBlur = profileFundo.filter(ImageFilter.GaussianBlur(radius=30))
        bg.paste(profileBlur, (0, 0))

        # xp_text = f'Faltam {} para chegar ao Nível {}:'.format(xp_remain, at_rank-1)

        bg_draw.rounded_rectangle(
            ((190, 768), (1089, 768 + 28)), 15, fill=(0, 45, 62))

        # xp_in = self.gradientLeft()
        # xp_im = Image.open(r"src/bg/xpFill-background.png")
        # im1 = xp_im.crop((0, 0, ((int(xp/needed_xp*100))*6), 81))
        # bg.paste(im1, (488, 558), im1)
        #
        # bg_draw.text((515, 503), xp_text, font=self.class_font_semibold, fill=(161, 177, 191))
        # bg_draw.text((870, 618), f"{xp}/{needed_xp}", font=self.class_font_bold_xp, anchor="ms", fill=(219, 239, 255))

        needed_xp = self.neededxp(int(rank))

        # Pré
        bg_draw.text((80, 797), "NÍVEL ",
                     font=self.class_font_montserrat_regular_nivel, anchor="ms", fill=(219, 239, 255))
        bg_draw.text((150, 797), f"{rank}",
                     font=self.class_font_role, anchor="ms", fill=(219, 239, 255))

        # Middle
        bg_draw.text((640, 855),
                     f"Faltam {int(needed_xp - xp):,} de XP para chegar ao Nível {rank + 1}".replace(
                         ",", "."),
                     font=self.class_font_role, anchor="ms", fill=(219, 239, 255))

        # Pós
        bg_draw.text((1089 + 70, 797), "NÍVEL ",
                     font=self.class_font_montserrat_regular_nivel, anchor="ms", fill=(219, 239, 255))
        bg_draw.text((1089 + 140, 797), f"{rank + 1}",
                     font=self.class_font_role, anchor="ms", fill=(219, 239, 255))

        xp_im = Image.open(rf"{imgxp}")
        # ((already_earned / to_reach) * 100)

        # print(xp_im.size[0])
        # print(int((xp / needed_xp) * xp_im.size[0]))
        # print(xp / needed_xp)

        im1 = xp_im.crop((0, 0, int((xp / needed_xp) * xp_im.size[0]), 81))
        bg.paste(im1, (190, 767), im1)

        color = xp_im.getpixel(((xp_im.size[0]) - 5, 7))

        if moldName is not None:
            rank_text = moldName.upper()
            bg_draw.text((635, 622), rank_text, font=self.RadiateSansBoldCondensed, fill=(
                color), anchor="ms", spacing=5)

        bg_draw.text((695, 730), f"{int(xptotal):,}".replace(",", "."),
                     font=self.XPRadiateSansBoldCondensed, anchor="rs", fill=(219, 239, 255))
        bg_draw.text((705, 730), "XP TOTAL",
                     font=self.class_font_montserrat_regular_nivel, anchor="ls", fill=(219, 239, 255))

        border_im = Image.open(r"{}".format(moldImg))
        bsize = (border_im.size[0], border_im.size[1])
        bg.paste(
            border_im, (int((1270 - bsize[0]) / 2) + 2, (int((892 - bsize[1]) / 2) - 150)), border_im)

        buffer = BytesIO()
        bg.save(buffer, 'png')
        buffer.seek(0)

        mask.close()
        bg.close()

        return buffer

drawlevel = drawLevel().level
neededxp = drawLevel().neededxp


# -*- coding: utf8 -*-
__author__ = "Hoseong Son <me@sookcha.com>"

import datetime
import xml.etree.ElementTree as ElementTree
from dateutil import parser
from icalendar import Calendar, Event
import requests

class Convert():
    def __init__(self, filename):
        self.filename = filename

    def get_subjects(self):
        result = []
        try:
            tree = ElementTree.parse(self.filename)
            root = tree.getroot()
        except:
            tree = ElementTree.fromstring(self.filename)
            root = tree

        for subject in root.iter('subject'):
            name = subject.find("name").get("value")
            single_subject = {}

            single_subject["name"] = name
            single_subject["professor"] = subject.find("professor").get("value")
            single_subject["info"] = list(map(
                lambda x: {
                    "day": x.get("day"),
                    "place" : x.get("place"),
                    "startAt": '{:02d}:{:02d}'.format(*divmod(int(x.get("starttime")) * 5, 60)),
                    "endAt": '{:02d}:{:02d}'.format(*divmod(int(x.get("endtime")) * 5, 60))
                }, subject.find("time").findall("data")
                )
            )
            result.append(single_subject)

        return result

    def get_calendar(self, timetable, start_date, end_date, hide_details=False):
        cal = Calendar()

        for item in timetable:
            for time in item["info"]:
                event = Event()
                event.add('summary', item["name"] if not hide_details else "수업")
                event.add('dtstart', parser.parse("%s %s" % (self.get_nearest_date(start_date, time["day"]), time["startAt"])))
                event.add('dtend', parser.parse("%s %s" % (self.get_nearest_date(start_date, time["day"]), time["endAt"])))
                event.add('rrule', {'freq': 'WEEKLY', 'until': parser.parse(end_date)})
                if time["place"] != "" and not hide_details:
                    event.add('location', time["place"])
                cal.add_component(event)

        return cal

    def export_calender_as_ics(self, cal, path):
        with open(path, 'wb') as f:
            f.write(cal.to_ical())

    def get_nearest_date(self, start_date, weekday):
        start_date = parser.parse(start_date)
        weekday = int(weekday)

        if start_date.weekday() >= weekday:
            if start_date.weekday() > weekday: start_date += datetime.timedelta(days=7)
            start_date -= datetime.timedelta(start_date.weekday() - weekday)
        else:
            start_date += datetime.timedelta(weekday - start_date.weekday())

        return start_date