import xgboost as xgb
import streamlit as st
import pandas as pd
import joblib

#Loading up the Regression model we created
model = joblib.load('xgb_model.pkl')

# Define the prediction function
def pred(age, job, marital, education_qual, call_type, day, mon, dur, num_calls, prev_outcome):
    if job=='blue-collar':
        job=0
    elif job=='entrepreneur':
        job=1
    elif job=='housemaid':
        job=2
    elif job=='services':
        job=3
    elif job=='technician':
        job=4
    elif job=='self-employed':
        job=5
    elif job=='admin.':
        job=6
    elif job=='management':
        job=7
    elif job=='unemployed':
        job=8
    elif job=='retired':
        job=9
    elif job=='student':
        job=10
    elif job=='unknown':
        job=0

    if marital=='married':
        marital=0
    elif marital=='divorced':
        marital=1
    elif marital=='single':
        marital=2

    if education_qual=='primary':
        education_qual=0
    elif education_qual=='secondary' or education_qual=='unknown':
        education_qual=1
    elif education_qual=='tertiary':
        education_qual=2

    if call_type=='unknown':
        call_type=0
    elif call_type=='telephone':
        call_type=1
    elif call_type=='cellular':
        call_type=2

    if mon=='may':
        mon=0
    elif mon=='jul':
        mon=1
    elif mon=='jan':
        mon=2
    elif mon=='nov':
        mon=3
    elif mon=='jun':
        mon=4
    elif mon=='aug':
        mon=5
    elif mon=='feb':
        mon=6
    elif mon=='apr':
        mon=7
    elif mon=='oct':
        mon=8
    elif mon=='sep':
        mon=9
    elif mon=='dec':
        mon=10
    elif mon=='mar':
        mon=11

    if prev_outcome=='unknown':
        prev_outcome=0
    elif prev_outcome=='failure':
        prev_outcome=1
    elif prev_outcome=='other':
        prev_outcome=2
    elif prev_outcome=='success':
        prev_outcome=3

    if num_calls>6:
        num_calls=6

    prediction= model.predict([[age, job, marital, education_qual, call_type, day, mon, dur, num_calls, prev_outcome]])
    print(prediction)

    return abs(prediction)

st.title('Customer Conversion Predictor')
# st.image()
st.header('Enter the characteristics of the Potential Customer:')

age= st.number_input("Age of the person : ", min_value=0, max_value=100, value=20)
job = st.selectbox("Customer's Job : ", ['management', 'technician', 'entrepreneur', 'blue-collar', 'unknown', 'retired',
                                         'admin.', 'services', 'self-employed', 'unemployed', 'housemaid', 'student'])
marital = st.selectbox("Customer's Marital Status :", ['married', 'single', 'divorced'])
education_qual = st.selectbox("Customer's Educational Status :", ['tertiary', 'secondary', 'unknown', 'primary'])
call_type = st.selectbox("Type of call :", ['unknown', 'cellular', 'telephone'])
day = st.number_input("Day of call made (1 to 31) : ", min_value=1, max_value=31, value=1)
mon = st.selectbox("Month of contact :",['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'])
dur = st.number_input("Duration of call in secs:", min_value=0)
num_calls = st.number_input("Number of calls made :", min_value=0, value=0)
prev_outcome = st.selectbox("Previous outcome of contact :", ['unknown', 'failure', 'other', 'success'])

if st.button('Predict Outcome'):
    result= pred(age, job, marital, education_qual, call_type, day, mon, dur, num_calls, prev_outcome)
    if (result[0])==1:
        st.success("The Customer is likely to get converted successfully.")
    else:
        st.warning("The Customer is unlikely to get converted.")


"""
Given an integer array nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once. You can return the answer in any order.

You must write an algorithm that runs in linear runtime complexity and uses only constant extra space.

Example 1:

Input: nums = [1,2,1,3,2,5]
Output: [3,5]
Explanation:  [5, 3] is also a valid answer.
Example 2:

Input: nums = [-1,0]
Output: [-1,0]
Example 3:

Input: nums = [0,1]
Output: [1,0]

Constraints:

2 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
Each integer in nums will appear twice, only two integers will appear once.
"""
from typing import List

class SingleNumberIII:
    def singleNumber(self, nums: List[int]) -> List[int]:
        ans = []
        idx = 0
        nums = sorted(nums)
        i = 0
        while i < len(nums):
            if i == len(nums) - 1:
                ans.append(nums[i])
                break
            else:
                if nums[i] != nums[i+1]:
                    ans.append(nums[i])
                    idx += 1
                    if(idx == 2):
                        break
                    else:
                        i -= 1
            i += 2

        return ans


from requests import get
from bs4 import BeautifulSoup
import pandas as pd
import os

def get_one_blog_article(url):
    '''This function takes in a url in string format, it provides a User-Agent to the URL and requests a response using the URL and headers.
    It then uses the BeautifulSoup library to parse the URL contents. Lastly, it creates an empty dictionary, then stores the article title,
    date published,and blog article contents in the dictionary. It returns the dictionary as output.
    '''

    headers = {'User-Agent': 'Innis Data Science cohort'}
    response = get(url, headers=headers)

    # Make a soup variable holding the response content
    soup = BeautifulSoup(response.text, 'html.parser')

    output = {}
    output['title'] = soup.select_one('h1.entry-title').text
    output['published'] = soup.select_one('.published').text
    output['contents'] = soup.select_one('div.entry-content').text

    return output

urls = [
    'https://codeup.com/codeup-news/codeup-start-dates-for-march-2022/',
    'https://codeup.com/featured/5-books-every-woman-in-tech-should-read/',
    'https://codeup.com/codeup-news/dallas-campus-re-opens-with-new-grant-partner/',
    'https://codeup.com/codeup-news/codeups-placement-team-continues-setting-records/',
    'https://codeup.com/it-training/it-certifications-101/'
]

def get_blog_articles(urls):
    '''This function takes in a list of urls in string format.
    It applies the get_one_blog_article function above to obtain the title and contents
    of each of the blog urls. It then returns a dataframe with the title and contents.
    '''

    # Create a list of dictionaries
    list_dic = [get_one_blog_article(url) for url in urls]
    # Return a data frame
    return pd.DataFrame(list_dic)   #.style.set_properties(**{'text-align':'left'})

# Take in ANY card
def parse_news_card(card, category):
    output = {}
    output['category'] = category
    output['title'] = card.select_one('a.clickable').text.strip()

    card_content = card.select_one('div.news-card-content')
    output['content'] = card_content.select_one('div').text
    output['author'] = card_content.select_one('.author').text
    output['published'] = card_content.select_one('.time').attrs['content']

    return output

# Need a function to obtain ANY CATEGORY
# take in a category and returns a list of dictionaries list[dict]
def parse_inshorts_page(category):
    url = 'https://inshorts.com/en/read/' + category
    response = get(url)
    soup = BeautifulSoup(response.text)
    articles = [parse_news_card(card, category) for card in soup.select('.news-card')]

    return articles

def get_news_articles():
    categories = ['business', 'sports', 'entertainment', 'technology']
    articles = []

    for category in categories:
        print(f'Getting articles for {category}')
        category_articles = parse_inshorts_page(category)
        articles.extend(category_articles)
    return pd.DataFrame(articles)


from flask import jsonify
from flask import request
from flask import render_template
from app import app
from cassandra.cluster import Cluster
cluster = Cluster(['ec2-34-225-164-189.compute-1.amazonaws.com'])
session = cluster.connect('pageranks')
session_subs = cluster.connect('reddit')

user_list = []
subreddit_list = []
year_list = ['2017', '2016', '2015', '2014', '2013', '2011', '2010', '2009', '2008']

class User(object):
    def __init__(self, name, avg08, avg09, avg10, avg11, avg12, avg13, avg14, avg15, avg16, avg17):
        self.name = name
        self.avg08 = avg08
        self.avg09 = avg09
        self.avg10 = avg10
        self.avg11 = avg11
        self.avg12 = avg12
        self.avg13 = avg13
        self.avg14 = avg14
        self.avg15 = avg15
        self.avg16 = avg16
        self.avg17 = avg17

    def string_out(self):
        return ["2008:    " + str(self.avg08), "2009:    " + str(self.avg09), "2010:    " + str(self.avg10),
         "2011:    " + str(self.avg11),"2012:    " + str(self.avg12), "2013:    " + str(self.avg13),
         "2014:    " + str(self.avg14),"2015:    " + str(self.avg15),"2016:    " + str(self.avg16),
         "2017:    " + str(self.avg17)]

class Subreddit(object):
    def __init__(self, name, adjacencies, year):
        self.name = name + " " + year
        self.adjacencies = sorted(adjacencies, key=lambda x:x[1])

    def get_similar(self):
        similar = []
        for adj, val in reversed(self.adjacencies):
            similar.append(adj)
        return similar

@app.route('/')
@app.route('/index')
def index():
    return render_template("index.html", user_list = user_list)

@app.route('/index', methods=['POST'])
def handle_data():
    reddit_username = request.form['search']
    reddit_username = '\'' + reddit_username + '\''
    stmt = ''' SELECT * FROM allranks WHERE name=''' + reddit_username
    response = session.execute(stmt)
    response_list = []
    for val in response:
    	response_list.append(val)
    if (len(response_list) == 0):
        return render_template("index.html", user_list = user_list)
    newuser = User(response_list[0].name, response_list[0].average_2008,
        response_list[0].average_2009, response_list[0].average_2010,
        response_list[0].average_2011, response_list[0].average_2012,
        response_list[0].average_2013, response_list[0].average_2014,
        response_list[0].average_2015, response_list[0].average_2016,
        response_list[0].average_2017)
    user_list.insert(0, newuser)
    return render_template("index.html", user_list = user_list)

@app.route('/subreddit')
def subreddit():
    return render_template("subreddit.html", subreddit_list = subreddit_list)

@app.route('/subreddit', methods=['POST'])
def handle_subreddit_data():
    sub = request.form['search_subreddit'] + ''
    if (';' in sub):
        new_subreddit = Subreddit('Stop trying to SQL inject my demo', [], 'Tigran!')
        subreddit_list.insert(0, new_subreddit)
        return render_template("subreddit.html", subreddit_list = subreddit_list)
    year = sub[:4]
    s_reddit = sub[5:]
    if (year not in year_list):
        new_subreddit = Subreddit('Stop trying to break my demo Tigran.', [], 'Queries must be in the form \'year:Subreddit\' or \'year Subreddit\'')
        subreddit_list.insert(0, new_subreddit)
        return render_template("subreddit.html", subreddit_list = subreddit_list)

    s_reddit = '\'' + s_reddit + '\''
    stmt = ''' SELECT *  FROM ''' + '''subs_''' + year + ''' WHERE name=''' + s_reddit
    response = session_subs.execute(stmt)
    response_list = []

    for val in response:
        response_list.append(val)
    if (len(response_list) == 0):
        return render_template("subreddit.html", subreddit_list = subreddit_list)

    new_adjacencies = []
    for item in response_list[0].adj:
        new_adjacencies.append((item, response_list[0].adj.get(item)))

    new_subreddit = Subreddit(response_list[0].name, new_adjacencies, year)
    subreddit_list.insert(0, new_subreddit)
    return render_template("subreddit.html", subreddit_list = subreddit_list)


# Programa para ler se o a no é BISSEXTO ou não.
# 1 importar a biblioteca datetime, escolher apenas a função date que é a que  será usada
from datetime import date
ano = int(input('Que ano quer analisar? '))
# usar a formula IF(SE) para analisar se o ano digitado for 0, será igual a date.today().year.
# date.today() irá pegar a data de hoje e o .year irá trazer o que eu quero da data de hoje que no caso é o ano
# então se o usuario digitar 0 o programa irá trazer o ano de hoje automaticamente.
if ano == 0:
    ano = date.today().year
# aqui é usado o se(IF) com o and para fazer um outro se(IF) dentro dele. analisar duas situações diferentes.
if ano % 4 == 0 and ano % 100 != 0 or ano % 400 == 0:
    print('O ano {} é BISSEXTO'.format(ano))
else:
    print('O ano {} não é BISSEXTO'.format(ano))


#function to pull data from cvs file
def listCharacters():
    people =csv.DictReader(open("starwars.csv","r"))
    for entry in people:
        print(entry)

#print out character name, actor name, and birthdate

#main
print(listCharacters())

#Part 2:
import datetime
def getBios():
    people=csv.DictReader(open("starwars.csv","r"))
    now=datetime.date.today()
    for entry in people:
        birth_parts=entry["birthday"].split("/")
        print(birth_parts)
        #print(birth_parts)
        birthday=datetime.date(int(birth_parts[2]),int(birth_parts[0]),int(birth_parts[1]))
        age = now - birthday
        print(entry["name"],"is played by",entry["character"],"and was born on",birthday.strftime("%B %d, %Y"),"and is",round(age.days/365),"years old")

#main
getBios()


import binascii
import collections
import io
import logging
import os
import struct

from . import usb_exceptions
from openhtf.util import argv

FASTBOOT_DOWNLOAD_CHUNK_SIZE_KB = 1024

ARG_PARSER = argv.module_parser()
ARG_PARSER.add_argument(
    '--fastboot_download_chunk_size_kb',
    default=FASTBOOT_DOWNLOAD_CHUNK_SIZE_KB,
    action=argv.StoreInModule,
    type=int,
    target='%s.FASTBOOT_DOWNLOAD_CHUNK_SIZE_KB' % __name__,
    help='Size of chunks to send when downloading fastboot images')

_LOG = logging.getLogger(__name__)

DEFAULT_MESSAGE_CALLBACK = lambda m: _LOG.info('Got %s from device', m)

class FastbootMessage(
    collections.namedtuple('FastbootMessage', [
        'message',
        'header',
    ])):
  pass

class FastbootProtocol(object):
  """Encapsulates the fastboot protocol."""
  FINAL_HEADERS = {'OKAY', 'DATA'}

  def __init__(self, usb):
    """Constructs a FastbootProtocol instance.

    Arguments:
      usb: UsbHandle instance.
    """
    self.usb = usb

  @property
  def usb_handle(self):
    """This instance's USB handle."""
    return self.usb

  def send_command(self, command, arg=None):
    """Sends a command to the device.

    Args:
      command: The command to send.
      arg: Optional argument to the command.
    """
    if arg is not None:
      command = '%s:%s' % (command, arg)
    self._write(io.StringIO(command), len(command))

  def handle_simple_responses(self,
                              timeout_ms=None,
                              info_cb=DEFAULT_MESSAGE_CALLBACK):
    """Accepts normal responses from the device.

    Args:
      timeout_ms: Timeout in milliseconds to wait for each response.
      info_cb: Optional callback for text sent from the bootloader.

    Returns:
      OKAY packet's message.
    """
    return self._accept_responses('OKAY', info_cb, timeout_ms=timeout_ms)

  # pylint: disable=too-many-arguments
  def handle_data_sending(self,
                          source_file,
                          source_len,
                          info_cb=DEFAULT_MESSAGE_CALLBACK,
                          progress_callback=None,
                          timeout_ms=None):
    """Handles the protocol for sending data to the device.

    Arguments:
      source_file: File-object to read from for the device.
      source_len: Amount of data, in bytes, to send to the device.
      info_cb: Optional callback for text sent from the bootloader.
      progress_callback: Callback that takes the current and the total progress
        of the current file.
      timeout_ms: Timeout in milliseconds to wait for each response.

    Raises:
      FastbootTransferError: When fastboot can't handle this amount of data.
      FastbootStateMismatchError: Fastboot responded with the wrong packet type.
      FastbootRemoteFailureError: Fastboot reported failure.
      FastbootInvalidResponseError: Fastboot responded with an unknown packet
        type.

    Returns:
      OKAY packet's message.
    """
    accepted_size = self._accept_responses(
        'DATA', info_cb, timeout_ms=timeout_ms)

    accepted_size = binascii.unhexlify(accepted_size[:8])
    accepted_size, = struct.unpack('>I', accepted_size)
    if accepted_size != source_len:
      raise usb_exceptions.FastbootTransferError(
          'Device refused to download %s bytes of data (accepts %s bytes)' %
          (source_len, accepted_size))
    self._write(source_file, accepted_size, progress_callback)
    return self._accept_responses('OKAY', info_cb, timeout_ms=timeout_ms)

  # pylint: enable=too-many-arguments

  def _accept_responses(self, expected_header, info_cb, timeout_ms=None):
    """Accepts responses until the expected header or a FAIL.

    Arguments:
      expected_header: OKAY or DATA
      info_cb: Optional callback for text sent from the bootloader.
      timeout_ms: Timeout in milliseconds to wait for each response.

    Raises:
      FastbootStateMismatchError: Fastboot responded with the wrong packet type.
      FastbootRemoteFailureError: Fastboot reported failure.
      FastbootInvalidResponseError: Fastboot responded with an unknown packet
        type.

    Returns:
      OKAY packet's message.
    """
    while True:
      response = self.usb.read(64, timeout_ms=timeout_ms)
      header = response[:4]
      remaining = response[4:]

      if header == 'INFO':
        info_cb(FastbootMessage(remaining, header))
      elif header in self.FINAL_HEADERS:
        if header != expected_header:
          raise usb_exceptions.FastbootStateMismatchError(
              'Expected %s, got %s' % (expected_header, header))
        if header == 'OKAY':
          info_cb(FastbootMessage(remaining, header))
        return remaining
      elif header == 'FAIL':
        info_cb(FastbootMessage(remaining, header))
        raise usb_exceptions.FastbootRemoteFailureError('FAIL: %s' % remaining)
      else:
        raise usb_exceptions.FastbootInvalidResponseError(
            'Got unknown header %s and response %s' % (header, remaining))

  def _handle_progress(self, total, progress_callback):  # pylint: disable=no-self-use
    """Calls the callback with the current progress and total ."""
    current = 0
    while True:
      current += yield
      try:
        progress_callback(current, total)
      except Exception:  # pylint: disable=broad-except
        _LOG.exception('Progress callback raised an exception. %s',
                       progress_callback)
        continue

  def _write(self, data, length, progress_callback=None):
    """Sends the data to the device, tracking progress with the callback."""
    if progress_callback:
      progress = self._handle_progress(length, progress_callback)  # pylint: disable=assignment-from-no-return
      next(progress)
    while length:
      tmp = data.read(FASTBOOT_DOWNLOAD_CHUNK_SIZE_KB * 1024)
      length -= len(tmp)
      self.usb.write(tmp)

      if progress_callback:
        progress.send(len(tmp))

class FastbootCommands(object):
  """Encapsulates the fastboot commands."""
  protocol_handler = FastbootProtocol

  def __init__(self, usb):
    """Constructs a FastbootCommands instance.

    Arguments:
      usb: UsbHandle instance.
    """
    self._usb = usb
    self._protocol = self.protocol_handler(usb)

  @property
  def handle(self):
    """This instance's USB handle."""
    return self._usb

  def close(self):
    """Close the USB handle."""
    self._usb.close()

  def _simple_command(self, command, arg=None, **kwargs):
    """Send a simple command."""
    self._protocol.send_command(command, arg)
    return self._protocol.handle_simple_responses(**kwargs)

  # pylint: disable=too-many-arguments
  def flash_from_file(self,
                      partition,
                      source_file,
                      source_len=0,
                      info_cb=DEFAULT_MESSAGE_CALLBACK,
                      progress_callback=None,
                      timeout_ms=None):
    """Flashes a partition from the file on disk.

    Args:
      partition: Partition name to flash to.
      source_file: Filename to download to the device.
      source_len: Optional length of source_file, uses os.stat if not provided.
      info_cb: See Download.
      progress_callback: See Download.
      timeout_ms: The amount of time to wait on okay after flashing.

    Returns:
      Download and flash responses, normally nothing.
    """
    if source_len == 0:
      # Fall back to stat.
      source_len = os.stat(source_file).st_size
    download_response = self.download(
        source_file,
        source_len=source_len,
        info_cb=info_cb,
        progress_callback=progress_callback)
    flash_response = self.flash(
        partition, info_cb=info_cb, timeout_ms=timeout_ms)
    return download_response + flash_response

  # pylint: enable=too-many-arguments

  def download(self,
               source_file,
               source_len=0,
               info_cb=DEFAULT_MESSAGE_CALLBACK,
               progress_callback=None):
    """Downloads a file to the device.

    Args:
      source_file: A filename or file-like object to download to the device.
      source_len: Optional length of source_file. If source_file is a file-like
        object and source_len is not provided, source_file is read into memory.
      info_cb: Optional callback accepting FastbootMessage for text sent from
        the bootloader.
      progress_callback: Optional callback called with the percent of the
        source_file downloaded. Note, this doesn't include progress of the
        actual flashing.

    Returns:
      Response to a download request, normally nothing.
    """
    if isinstance(source_file, str):
      source_len = os.stat(source_file).st_size
      source_file = open(source_file)

    if source_len == 0:
      # Fall back to storing it all in memory :(
      data = source_file.read()
      source_file = io.StringIO(data)
      source_len = len(data)

    self._protocol.send_command('download', '%08x' % source_len)
    return self._protocol.handle_data_sending(
        source_file, source_len, info_cb, progress_callback=progress_callback)

  def flash(self, partition, timeout_ms=None, info_cb=DEFAULT_MESSAGE_CALLBACK):
    """Flashes the last downloaded file to the given partition.

    Args:
      partition: Partition to flash.
      timeout_ms: Optional timeout in milliseconds to wait for it to finish.
      info_cb: See Download. Usually no messages.

    Returns:
      Response to a download request, normally nothing.
    """
    return self._simple_command(
        'flash', arg=partition, info_cb=info_cb, timeout_ms=timeout_ms)

  def erase(self, partition, timeout_ms=None):
    """Erases the given partition."""
    self._simple_command('erase', arg=partition, timeout_ms=timeout_ms)

  def get_var(self, var, info_cb=DEFAULT_MESSAGE_CALLBACK):
    """Returns the given variable's definition.

    Args:
      var: A variable the bootloader tracks, such as version.
      info_cb: See Download. Usually no messages.

    Returns:
      Value of var according to the current bootloader.
    """
    return self._simple_command('getvar', arg=var, info_cb=info_cb)

  def oem(self, command, timeout_ms=None, info_cb=DEFAULT_MESSAGE_CALLBACK):
    """Executes an OEM command on the device.

    Args:
      command: The command to execute, such as 'poweroff' or 'bootconfig read'.
      timeout_ms: Optional timeout in milliseconds to wait for a response.
      info_cb: See Download. Messages vary based on command.

    Returns:
      The final response from the device.
    """
    return self._simple_command(
        'oem %s' % command, timeout_ms=timeout_ms, info_cb=info_cb)

  def continue_(self):
    """Continues execution past fastboot into the system."""
    return self._simple_command('continue')

  def reboot(self, target_mode=None, timeout_ms=None):
    """Reboots the device.

    Args:
        target_mode: Normal reboot when unspecified (or None). Can specify other
          target modes, such as 'recovery' or 'bootloader'.
        timeout_ms: Optional timeout in milliseconds to wait for a response.

    Returns:
        Usually the empty string. Depends on the bootloader and the target_mode.
    """
    return self._simple_command(
        'reboot', arg=target_mode, timeout_ms=timeout_ms)

  def reboot_bootloader(self, timeout_ms=None):
    """Reboots into the bootloader, usually equiv to Reboot('bootloader')."""
    return self._simple_command('reboot-bootloader', timeout_ms=timeout_ms)


from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),

    path('', include('user.urls')),
    path('courses/', include('course.urls')),

    path('accounts/', include('allauth.urls')),

    path('api/', include('course.api.urls')),
]


"""
Input Pipeline Handler for Localization CNN
Author: Edward Ferdian
Date:   01/06/2018
"""
import tensorflow as tf
import h5py
import numpy as np
from random import randint
import scipy.ndimage as ndimage
import math

class InputPipelineHandler:
    # constructor
    def __init__(self, batch_size):
        self.batch_size = batch_size

    def initialize_dataset(self, all_files, indexes, training=False):
        """
            Input pipeline.
            This function accepts a list of filenames with index to read.
            The _training_data_load_wrapper will read the filename-index pair and load the data.
        """
        ds = tf.data.Dataset.from_tensor_slices((all_files, indexes))

        if training:
            ds = ds.shuffle(1000)

        # Run the mapping functions on each data
        ds = ds.map(self.training_data_load_wrapper, num_parallel_calls=4)

        # ds = ds.map(_adjust_bbox_wrapper) # we don't do this anymore, the bbox in the dataset has been adjusted during prepare_data

        if training:
            ds = ds.map(self.augmentation_wrapper)
            # TODO: add more augmentation here (e.g. flip, translation, normalize, contrast, zoom, etc)

        ds = ds.map(self._resize_function)

        ds = ds.batch(batch_size=self.batch_size).prefetch(10)

        # Do not use repeat with initializable operator. Use 'while True' to exhaust the list
        return ds.make_initializable_iterator()

    # --- Wrapper functions for the tf.dataset input pipeline ---
    def training_data_load_wrapper(self, fpath, idx):
        # print('wrapper', idx)
        return tf.py_func(func=self.load_hd5_img_and_bbox_from_sequence,
            inp=[fpath, idx], Tout=[tf.float32, tf.float32])

    def augmentation_wrapper(self, img, coords):
        return tf.py_func(func=self._rotate_img_and_bbox,
            inp=[img, coords], Tout=[tf.float32, tf.float32])

    # def _adjust_bbox_wrapper(img, coords):
    #     return tf.py_func(func=self._adjust_bounding_box,
    #         inp=[img, coords, bbox_adjustment], Tout=[tf.float32, tf.float32])

    # --- end of wrapper functions ---

    def load_hd5_img_and_bbox_from_sequence(self, fpath, idx):
        '''
            Load an image from a pair of filename-index.
            Only the ED frame is taken from the image sequence.

        '''
        hd5path = fpath
        with h5py.File(hd5path, 'r') as hl:
            # [n, time=0, 256, 256]
            ed_img = np.asarray(hl.get('image_seqs')[idx,0,:,:]) # we only need the first frame
            # [n, 4]
            bbox = np.asarray(hl.get('bbox_corners')[idx])

        return ed_img.astype('float32'), bbox.astype('float32')

    def _resize_function(self, img, coords):
        img = tf.reshape(img, [256, 256])
        coords = tf.reshape(coords, [4])
        return img, coords

    def _rotate_img_and_bbox(self, img, bbox):
        """
            Rotate the image and bounding box.
            Currently it only does 90, 180, and 270 degrees.
        """
        rnd = randint(0,7)
        if (rnd == 0 or rnd > 3):
            # we give higher chance the image is not being rotated
            return img, bbox
        else:
            # we do only rotation for 90, 180, and 270 for now
            angle = rnd * 90
            new_img = ndimage.rotate(img, angle, reshape=False)
            new_bbox = self.rotate_coords_bbox(img.shape, bbox, -angle)

            return new_img, new_bbox.astype('float32')

    def rotate_coords_bbox(self, img_shape, coords, angle):
        center_point = np.asarray(img_shape)/2

        point = [coords[0], coords[1]]
        x1 = self.rotate_single_point(point, angle, center_point)

        point = [coords[2], coords[3]]
        x2 = self.rotate_single_point(point, angle, center_point)

        min_x = min(x1[0], x2[0])
        min_y = min(x1[1], x2[1])

        max_x = max(x1[0], x2[0])
        max_y = max(x1[1], x2[1])

        new_coords = np.asarray([min_x, min_y, max_x, max_y])

        return new_coords

    def rotate_single_point(self, point,angle, centerPoint):
        """
            Rotates a point around another centerPoint. Angle is in degrees.
            Rotation is counter-clockwise
        """
        angle = math.radians(angle)
        temp_point = point[0]-centerPoint[0] , point[1]-centerPoint[1]
        temp_point = ( temp_point[0]*math.cos(angle)-temp_point[1]*math.sin(angle) , temp_point[0]*math.sin(angle)+temp_point[1]*math.cos(angle))
        temp_point = temp_point[0]+centerPoint[0] , temp_point[1]+centerPoint[1]
        return temp_point


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

# Copyright 2014, Cercle Informatique ASBL. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This software was made by hast, C4, ititou at UrLab, ULB's hackerspace

import os
import sys
import json
from BeautifulSoup import BeautifulSoup

data = {}
a = 0
total = len(os.listdir("./cours/"))
for cours in os.listdir("./cours/"):
    a += 1
    sys.stdout.write("%s/%s\r" % (a, total))
    sys.stdout.flush()
    data[cours] = {}
    soup = BeautifulSoup(open("./cours/" + cours).read())
    if soup.find('table', 'bordertable') is None:
        continue
    for line in filter(lambda x: len(x('td')) == 2 and x.td.text, soup.find('table', 'bordertable')('tr')):
        key, value = line('td')
        data[cours][key.text] = value.text
    data[cours]["Nom"] = soup.find('td','dbtitle').p.childGenerator().next()

sys.stdout.write("\n")
open("cours.json", "w").write(json.dumps(data))


# -*- coding: utf-8 -*-
import collections
import subprocess

import pytest

def pytest_addoption(parser):
    group = parser.getgroup("circleci-parallelized")
    group.addoption(
        "--circleci-parallelize",
        dest="circleci_parallelize",
        action="store_true",
        default=False,
        help="Enable parallelization across CircleCI containers.",
    )

def get_verbosity(config):
    return config.option.verbose

def circleci_parallelized_enabled(config):
    return config.getoption("circleci_parallelize")

def pytest_report_collectionfinish(config, startdir, items):
    if circleci_parallelized_enabled(config):
        verbosity = get_verbosity(config)
        if verbosity == 0:
            return "running {} items due to CircleCI parallelism".format(len(items))
        elif verbosity > 0:
            return "running {} items due to CircleCI parallelism: {}".format(
                len(items), ", ".join(map(get_class_name, items))
            )
    else:
        return ""

def get_class_name(item):
    class_name, module_name = None, None
    for parent in reversed(item.listchain()):
        if isinstance(parent, pytest.Class):
            class_name = parent.name
        elif isinstance(parent, pytest.Module):
            module_name = parent.module.__name__
            break

    if class_name:
        return "{}.{}".format(module_name, class_name)
    else:
        return module_name

def filter_tests_with_circleci(test_list):
    circleci_input = "\n".join(test_list).encode("utf-8")
    p = subprocess.Popen(
        [
            "circleci",
            "tests",
            "split",
            "--split-by=timings",
            "--timings-type=classname",
        ],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
    )
    circleci_output, _ = p.communicate(circleci_input)
    return [
        line.strip() for line in circleci_output.decode("utf-8").strip().split("\n")
    ]

@pytest.hookimpl(hookwrapper=True)
def pytest_cmdline_main(config):
    outcome = yield config
    exit_code = outcome.get_result()

    # Exit Code 5 indicates no tests were collected
    # https://docs.pytest.org/en/7.1.x/reference/exit-codes.html
    # If there are more workers than tests, this can cause "No Tests" error
    # This is fine, and we cast it to an OK exit code
    if circleci_parallelized_enabled(config) and exit_code == 5:
        outcome.force_result(0)

def pytest_collection_modifyitems(session, config, items):
    if not circleci_parallelized_enabled(config):
        return

    class_mapping = collections.defaultdict(list)
    for item in items:
        class_name = get_class_name(item)
        class_mapping[class_name].append(item)

    filtered_tests = filter_tests_with_circleci(class_mapping.keys())

    new_items = []
    for name in filtered_tests:
        new_items.extend(class_mapping[name])

    items[:] = new_items


import os
import sys
from PIL import Image
import numpy as np
import random

if __name__ == '__main__':
    folder_name = ['chino', 'chiya', 'cocoa', 'maya', 'megu', 'rize', 'syaro']

    csv = []

    for folder in folder_name:
        folder_path = './anime_picture_face/' + folder + '/'
        files = os.listdir(folder_path)
        for idx, file in enumerate(files):
            try:
                img_txt = str(folder_name.index(folder)) + ","
                img = Image.open(folder_path+file)
                img = img.resize((32, 32))
                img_array = np.asarray(img).reshape(-1)
                for _idx, number in enumerate(img_array):
                    if _idx != 0: img_txt += ","
                    img_txt += str(number)
                csv.append(img_txt)
            except OSError:
                pass

    random.shuffle(csv)
    with open("dataset.csv", "w") as f:
        for data in csv:
            f.write(data + "\n")


import time
import sys

import chess

from util import fen4, move_list_to_sans

import evaluate

from engine import Engine, SearchStats

import cProfile

class Game:
    def __init__(self, board = chess.Board(), config_b = {"time-limit-s": 120}, config_w = {"time-limit-s": 60}):
        self.board = board
        # TODO - docs reckon this only copies the base-board - what about previous moves?
        self.engines = [Engine(board.copy(), config_b), Engine(board.copy(), config_w)]

    def play(self):
        total_engine_time_s = 0
        print_board = True

        w_engine = self.engines[chess.WHITE]
        b_engine = self.engines[chess.BLACK]

        while True:
            legal_move_sans = [self.board.san(m) for m in self.board.legal_moves]

            print()

            if print_board:
                print(self.board.fen())
                # print()
                # print(self.board)
                print()
                print(self.board.unicode(invert_color=True, empty_square='.')) # Unicode chars seems backwards; Don't seem to have the default empty_square unicode in my font
                print()
            print_board = True

            if self.board.is_game_over():

                reason = 'unknown'
                if self.board.is_checkmate():
                    reason = 'checkmate'
                elif self.board.is_stalemate():
                    reason = 'stalemate'
                elif self.board.is_insufficient_material():
                    reason = 'insufficient material'
                elif self.board.is_seventyfive_moves():
                    reason = '75 moves played without progress'
                elif self.board.is_fivefold_repetition():
                    reason = '5-fold repetition'

                print("Game over: %s (%s)" % (self.board.result(), reason))

                print()
                print("Moves: %s" % move_list_to_sans(self.board.root(), self.board.move_stack))
                return

            print("%s to move" % ["Black", "White"][self.board.turn])
            print()
            w_val = w_engine.static_eval()
            b_val = b_engine.static_eval()
            print("Static eval   - positive is White advantage: white engine %d cp black engine %d cp" % (w_val, b_val))
            sign = [-1, 1][w_engine.board.turn]
            w_qval = w_engine.quiesce_alphabeta(SearchStats(1, w_engine.MAX_QDEPTH), 0, w_val * sign) * sign
            b_qval = b_engine.quiesce_alphabeta(SearchStats(1, b_engine.MAX_QDEPTH), 0, b_val * sign) * sign
            print("Quiesced eval - positive is White advantage: white engine %d cp black engine %d cp" % (w_qval, b_qval))
            print()
            print("Legal moves: %s" % " ".join(legal_move_sans))
            print()
            move_san = input("Enter move - SAN format - or 'pass' or 'engine': ")
            print()

            if move_san == "pass":
                # null move - let the other side move
                if self.board.is_check():
                    print()
                    print(">>> Illegal to 'pass' when in check <<<")
                    print_board = False
                    continue

                move = chess.Move.null()

            elif move_san == 'engine':
                engine = self.engines[self.board.turn]
                engine_move, val, pv, stats = engine.gen_move()

                move = engine_move

            else:
                if not move_san in legal_move_sans:
                    print()
                    print(">>> Illegal move \"%s\" entered <<<" % move_san)
                    print_board = False
                    continue

                move = self.board.parse_san(move_san)

            self.board.push(move)
            w_engine.make_move(move)
            b_engine.make_move(move)

def main():
    print("Hallo RPJ - let's play chess")
    # game = Game(chess.Board(), 3*60)
    game = Game(chess.Board())
    # game = Game(chess.Board("2r1r1k1/2bn4/R1p1p3/2P2p1p/1P3P2/3B2PP/8/2BR2K1 w - - 1 32"))
    # game = Game(chess.Board("r1bqk2r/ppp2ppp/2nbpn2/3p4/3P4/2N1PN2/PPP1BPPP/R1BQK2R w KQkq - 2 6"))
    game.play()

if __name__ == "__main__":
    main()
    # cProfile.run("main()")


from ml_collections.config_dict import ConfigDict
import inspect
from e3nn import o3
import torch
from torch_runstats.scatter import scatter
import math
import numpy as np

def setSeed(seed):
  torch.manual_seed(seed)
  np.random.seed(seed)
  torch.cuda.manual_seed_all(seed)
  torch.backends.cudnn.deterministic = True

def getScaler(operations):
  """
  This function normalizes data by scaling and shifting.
  Example args: [('N', ('shift', 'CA', '-1')), ('CA', ('scale', 25))]
  """
  def scaler(batch):
    batch = batch.clone()
    node_segment = batch.nodeSegment()
    for ops in operations:
      key, op = ops
      if op[0] == 'scale':
        if isinstance(key, tuple) or isinstance(key, list):
          for tmp in key:
            batch[tmp] = batch[tmp]*op[1]
        else:
          batch[key] = batch[key]*op[1]
      elif op[0] == 'shift':
        if op[1] == 'mean':
          n_nodes = batch['_n_nodes'].view(-1, 1)
          center = scatter(batch[key], node_segment, dim=0, reduce='sum')
          center = center/n_nodes
          batch[key] = batch[key] - center[node_segment]
        elif op[1] in batch:
          sign = 1
          if len(op) == 3:
            sign = op[2]
          batch[key] = batch[key] + sign * batch[op[1]]
        else:
          raise ValueError()
      else:
        raise ValueError()
    return batch
  return scaler

def insertAfter(lst, key, item):
    for i, layer in enumerate(lst):
        if layer[0] == key:
            lst = lst[:i+1] + [item] + lst[i+1:]
            return lst
    raise ValueError(f'Key {key} not found.')

def replace(lst, key, item):
    for i, layer in enumerate(lst):
        if layer[0] == key:
            lst = lst[:i] + [item] + lst[i+1:]
            return lst
    raise ValueError(f'Key {key} not found.')

def transformMatrix(irreps_l, irreps_r, mat, H):
    D_l = irreps_l.D_from_matrix(mat)
    D_r = irreps_r.D_from_matrix(mat)
    return D_l @ H @ D_r.transpose(-1, -2)

def tanhlu(x):
    return torch.tanh(x) * torch.abs(x)

@torch.jit.script
def ShiftedSoftPlus(x):
    return torch.nn.functional.softplus(x) - math.log(2.0)

activations = {
    "abs": torch.abs,
    "tanh": torch.tanh,
    "ssp": ShiftedSoftPlus,
    "silu": torch.nn.functional.silu,
    "tanhlu": tanhlu,
}

def tp_path_exists(irreps_in1, irreps_in2, ir_out):
    irreps_in1 = o3.Irreps(irreps_in1).simplify()
    irreps_in2 = o3.Irreps(irreps_in2).simplify()
    ir_out = o3.Irrep(ir_out)

    for _, ir1 in irreps_in1:
        for _, ir2 in irreps_in2:
            if ir_out in ir1 * ir2:
                return True
    return False

def build(node, **kwargs):
    """Instantiates a layer using its config node"""
    if isinstance(node, dict) or isinstance(node, ConfigDict):
        func = node["module"]
        kwargs.update(**node)

    elif isinstance(node, list) or isinstance(node, tuple):
        func = node[0]
        args = node[1:]

    else:
        func = node

    args = []
    if "module" in kwargs:
        kwargs.pop("module")
    kwargs = pruneArgs(func, **kwargs)
    return func(*args, **kwargs)

def pruneArgs(_func=None, prefix="", **kwargs):
    if not prefix == "":
        args = {}
        for key, value in kwargs.items():
            if key.startswith(prefix):
                key = key[len(prefix) + 1 :]
                args[key] = value
    else:
        args = kwargs

    if not _func is None:
        arg_spec = inspect.getfullargspec(_func)
        if arg_spec.varkw:
            return args
        else:
            pnames = inspect.signature(_func).parameters
            return {key: args[key] for key in args if key in pnames}
    return args

def keyMap(dic, key_mapping):
    if isinstance(dic, dict):
        result = {}
        for key, value in dic.items():
            if key in key_mapping:
                new_key = key_mapping[key]
                if isinstance(new_key, str):
                    result[new_key] = value
                else:
                    for item in new_key:
                        result[item] = value
            else:
                result[key] = value
        return result
    else:  # an instance of data.Data
        attrs = keyMap(dic.attrs, key_mapping)
        data = keyMap(dic.data, key_mapping)
        return type(dic)(attrs, **data)

def _countParameters(module):
    return sum([param.numel() for param in module.parameters() if param.requires_grad])

def countParameters(model):
    for name, module in model.named_modules():
        cnt = _countParameters(module)
        if cnt > 0:
            print(name, cnt)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Mar 13 15:09:45 2022

@author: liyuzhe
"""
import os
import random
import numpy as np
import torch
from tqdm.auto import tqdm
from torch_geometric import seed_everything
import matplotlib.pyplot as plt

def adjust_learning_rate(init_lr, optimizer, iteration,seperation):
    lr = max(init_lr * (0.9 ** (iteration//seperation)), 0.0001)
    for param_group in optimizer.param_groups:
        param_group["lr"] = lr
    return lr

class EarlyStopping:
    """
    Early stops the training if loss doesn't improve after a given patience.
    """
    def __init__(self, patience=10, verbose=False, checkpoint_file=''):
        """
        Parameters
        ----------
        patience
            How long to wait after last time loss improved. Default: 10
        verbose
            If True, prints a message for each loss improvement. Default: False
        """
        self.patience = patience
        self.verbose = verbose
        self.counter = 0
        self.best_score = None
        self.early_stop = False
        self.loss_min = np.Inf
        self.checkpoint_file = checkpoint_file

    def __call__(self, loss, model):
        # loss=loss.cpu().detach().numpy()
        if np.isnan(loss):
            self.early_stop = True
        score = -loss

        if self.best_score is None:
            self.best_score = score
            self.save_checkpoint(loss, model)
        elif score < self.best_score:
            self.counter += 1
            if self.verbose:
                print(f'EarlyStopping counter: {self.counter} out of {self.patience}')
            if self.counter > self.patience:
                self.early_stop = True
                model.load_model(self.checkpoint_file)
        else:
            self.best_score = score
            self.save_checkpoint(loss, model)
            self.counter = 0

    def save_checkpoint(self, loss, model):
        '''
        Saves model when loss decrease.
        '''
        if self.verbose:
            print(f'Loss decreased ({self.loss_min:.6f} --> {loss:.6f}).  Saving model ...')
        torch.save(model.state_dict(), self.checkpoint_file)
        self.loss_min = loss

def train_SPACE(model, train_data, outdir, epoch=5000,lr=0.005, a=0.5,loss_type='MSE',patience=50, GPU=0,seed=42, verbose=False):

    np.random.seed(seed)
    torch.manual_seed(seed)
    random.seed(seed)
    seed_everything(seed)
    if torch.cuda.is_available(): # cuda device
        device='cuda'
        torch.cuda.set_device(GPU)
        torch.cuda.manual_seed(seed)
        torch.cuda.manual_seed_all(seed)
    else:
        device='cpu'

    torch.set_num_threads(1)  # Suggested for issues with deadlocks, etc.

    model.to(device)
    model.train()
    early_stopping = EarlyStopping(patience=patience, checkpoint_file=os.path.join(outdir,'model.pt'))

    y_loss = {}  # loss history
    y_loss['feature_loss'] = []
    y_loss['graph_loss'] = []
    y_loss['loss']=[]
    x_epoch = []
    fig = plt.figure()

    for epoch in tqdm(range(0, epoch+1)):
        epoch_loss = 0.0
        loss1 = 0.0
        loss2 = 0.0
        optimizer = torch.optim.Adam(model.parameters(), lr=lr, weight_decay=5e-4)
        epoch_lr=adjust_learning_rate(lr, optimizer, epoch, seperation=50)

        x, edge_index = train_data.x.to(torch.float).to(device), train_data.edge_index.to(torch.long).to(device)
        optimizer.zero_grad()

        z, _ = model.encode(x, edge_index)
        graph_loss = model.graph_loss(z, train_data.pos_edge_label_index) * a
        loss = graph_loss

        reconstructed_features = model.decoder_x(z)
        if loss_type=='BCE':
            feature_loss = torch.nn.functional.binary_cross_entropy(reconstructed_features, x) * 10
        elif loss_type=='MSE':
            feature_loss = torch.nn.functional.mse_loss(reconstructed_features, x) * 10

        loss += feature_loss
        loss.backward()
        optimizer.step()
        epoch_loss += loss.item()

        loss1 += graph_loss.item()
        loss2 += feature_loss.item()

        # if epoch%50==0:
        #     if verbose:
        #         print('Epoch {:03d} -- Total epoch loss: {:.4f} -- Feature decoder epoch loss: {:.4f} -- Graph decoder epoch loss: {:.4f} -- epoch lr: {:.4f}'.format(epoch, epoch_loss, feature_loss, epoch_loss-feature_loss, epoch_lr))
        #     else:
        #         print('====> Epoch: {}, Loss: {:.4f}'.format(epoch, epoch_loss))
        # early_stopping(epoch_loss, model)
        # if early_stopping.early_stop:
        #     print('EarlyStopping: run {} iteration'.format(epoch))
        #     break
        if verbose:
            if epoch%50==0:
                print('====> Epoch: {}, Loss: {:.4f}'.format(epoch, epoch_loss))
        early_stopping(epoch_loss, model)
        # plot loss
        y_loss['feature_loss'].append(loss2)
        y_loss['graph_loss'].append(loss1)
        y_loss['loss'].append(epoch_loss)
        x_epoch.append(epoch)
        plt.plot(x_epoch, y_loss['loss'], 'go-', label='loss',linewidth=1, markersize=2)
        plt.plot(x_epoch, y_loss['graph_loss'], 'ro-', label='graph_loss',linewidth=1, markersize=2)
        plt.plot(x_epoch, y_loss['feature_loss'], 'bo-', label='feature_loss',linewidth=1, markersize=2)
        if len(x_epoch)==1:
            plt.legend()

        if early_stopping.early_stop:
            print('====> Epoch: {}, Loss: {:.4f}'.format(epoch, epoch_loss))
            print('EarlyStopping: run {} iteration'.format(epoch))
            break

    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    fig.savefig(os.path.join(outdir, 'train_loss.pdf'))

    return device


import matplotlib.pyplot as plt
import pandas as pd
from sklearn import svm  # sử dụng phân lớp
from sklearn.model_selection import train_test_split  # chú ý
from sklearn.preprocessing import LabelEncoder

# 10a
df = pd.read_csv('../data/Iris.csv')
print(df)
# 10b: chuyển đổi giá trị phạm trù sang nhãn số cho thuận lợi tính toán khi cần
df["Species_num"] = LabelEncoder().fit_transform(df["Species"])
print(df)

x = df[['SepalWidthCm', 'SepalLengthCm', 'PetalLengthCm', 'PetalWidthCm']]
y = df['Species_num']

x0 = x[y == 0]
x1 = x[y == 1]
x2 = x[y == 2]
plt.plot(x0['SepalLengthCm'], x0['SepalWidthCm'], 'b^', markersize=4, alpha=.8)
plt.plot(x1['SepalLengthCm'], x1['SepalWidthCm'], 'go', markersize=4, alpha=.8)
plt.plot(x2['SepalLengthCm'], x1['SepalWidthCm'], 'r^', markersize=4, alpha=.8)

plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.title('Nhãn lớp 0,lớp 1 và lớp 2')
plt.plot()
plt.show()
# 10c.
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)
print(x_train)
print(y_train)
# 10d

svc = svm.SVC(kernel='linear', C=1.0).fit(x_train, y_train)
y_pred = svc.predict(x_test)
print('kiem tra trung khop giua y_test và y_pred')
print(y_test - y_pred)

# score_train=svc.score(x_train, y_train)
# print('SVM:score train:',score_train*100,'%')
# score_test=svc.score(x_test, y_test)
# print('SVM:score test:',score_test*100,'%')


import tkinter
from tkinter.messagebox import showinfo as alert
from tkinter.messagebox import askyesno as question
from tkinter.simpledialog import askstring as prompt
import customtkinter

# Es la gala de eliminación del Gran Utniano y la producción nos pide un programa para contar los votos de los
# televidentes y saber cuál será el participante que deberá abandonar la casa más famosa del mundo.
# Los participantes en la placa son: Giovanni, Gianni y Facundo.
# Fausto no fue nominado y Marina no está en la placa esta semana por haber ganado la inmunidad.

# Cada televidente que vota deberá ingresar:
# Nombre del votante
# Edad del votante (debe ser mayor a 13)
# Género del votante (Masculino, Femenino, Otro)
# El nombre del participante a quien le dará el voto negativo (Debe estar en placa)
# No se sabe cuántos votos entrarán durante la gala.

# Se debe informar al usuario:
# El promedio de edad de las votantes de género Femenino
# Cantidad de personas de género masculino entre 25 y 40 años que votaron a Giovanni o a Facundo.
# Nombre del votante más joven qué votó a Gianni.
# Nombre de cada participante y porcentaje de los votos qué recibió.
# El nombre del participante que debe dejar la casa (El que tiene más votos)

class App(customtkinter.CTk):

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

        # configure window
        self.title("UTN FRA")

        self.btn_mostrar = customtkinter.CTkButton(master=self, text="Mostrar", command=self.btn_mostrar_on_click)
        self.btn_mostrar.grid(row=2, pady=20, columnspan=2, sticky="nsew")

    def btn_mostrar_on_click(self):
        lista_nombres = ["lala", "julieta", "pepe", "coco", "ñaño", "pato"]
        lista_edades = [25, 23, 27, 29, 30, 15]
        lista_generos = ["femenino", "femenino", "masculino", "masculino", "masculino", "masculino"]
        lista_votos = ["facundo", "giovanni", "facundo", "gianni", "gianni", "facundo"]

        # for _ in range(1):
        #     nombre = prompt("Mostrar", "Nombre")
        #     while nombre == None or not nombre.isalpha():
        #         nombre = prompt("Mostrar", "Nombre")
        #     lista_nombres.append(nombre)

        #     edad = prompt("Mostrar", "Edad")
        #     while edad == None or not edad.isdigit() or int(edad) < 13:
        #         edad = prompt("Mostrar", "Edad")
        #     edad = int(edad)
        #     lista_edades.append(edad)

        #     genero = prompt("Mostrar", "Género")
        #     while genero == None or genero not in ["masculino", "femenino", "otro"]:
        #         genero = prompt("Mostrar", "Género")
        #     lista_generos.append(genero)

        #     voto = prompt("Mostrar", "a quien votas?")
        #     while voto == None or not voto.isalpha() or voto not in ["giovanni", "gianni", "facundo"]:
        #         voto = prompt("Mostrar", "a quien votas?")
        #     lista_votos.append(voto)

        promedio_de_edad = 0
        i = 0
        contador = 0
        contador_mujeres = 0
        votante_mas_joven = 1000
        for genero in lista_generos:
            if genero == "femenino":
                contador_mujeres +=1
                edad = lista_edades[i]
                promedio_de_edad += edad
            if genero == "masculino":
                edad = lista_edades[i]
                if edad >= 25 and edad <= 40:
                    voto = lista_votos[i]
                    if voto == "giovanni" or voto == "facundo":
                        contador += 1

            i+=1
        promedio_de_edad = promedio_de_edad/contador_mujeres

        if promedio_de_edad > 0:
            print (f"el promedio de edad en votantes mujeres es {promedio_de_edad}")
        else:
            print ("No se han registrado votantes mujeres")

        if contador > 0:
            print (f"la Cantidad de personas de género masculino entre 25 y 40 años que votaron a Giovanni o a Facundo: {contador}")
        else:
            print ("No se han registrado votantes de género masculino entre 25 y 40 años que votaron a Giovanni o a Facundo")

        i = 0
        contador_gianni = 0
        contador_facu = 0
        contador_gio = 0
        promedio_de_votos_gio = 0
        promedio_de_votos_facu = 0
        promedio_de_votos_gia = 0

        for voto in lista_votos:
            if voto == "gianni":
                edad = lista_edades[i]
                contador_gianni += 1
                if edad < votante_mas_joven:
                    votante_mas_joven = edad
                    nombre_del_votante_mas_joven = lista_nombres[i]
            if voto == "facundo":
                contador_facu += 1
            elif voto == "giovanni":
                contador_gio += 1
            i += 1

        promedio_de_votos_gio = contador_gio/len(lista_votos) * 100
        promedio_de_votos_facu = contador_facu/len(lista_votos) * 100
        promedio_de_votos_gia = contador_gianni/len(lista_votos) * 100
        print (f"el nombre del votante más joven qué votó a Gianni es {nombre_del_votante_mas_joven}")
        print (f"promedio de votos de facu: {promedio_de_votos_facu}% - promedio de votos de gio {promedio_de_votos_gio}% - promedio de votos de gia {promedio_de_votos_gia}%")

        if promedio_de_votos_gio > promedio_de_votos_gia and promedio_de_votos_gio > promedio_de_votos_facu:
            print (f"el nombre del participante que debe dejar la casa es Giovanni con {contador_gio} votos")
        elif promedio_de_votos_facu > promedio_de_votos_gia:
            print (f"el nombre del participante que debe dejar la casa es Facundo con {contador_facu} votos")
        else:
            print (f"el nombre del participante que debe dejar la casa es Gianni con {contador_gianni} votos")

if __name__ == "__main__":
    app = App()
    app.geometry("300x300")
    app.mainloop()


import json
from builtins import list
from datetime import datetime
import bisect
import copy
import numpy as np
import os
from os import listdir
from os.path import isfile, join
import MarketStateManager
import TransactionBasics

PeakFeatureCount = TransactionBasics.PeakFeatureCount
MaxMinDataLen = 8
UpSideDataLen = 6
TotalExtraFeatureCount = PeakFeatureCount + MaxMinDataLen + UpSideDataLen
percent = 0.01

class BuySellHandler:
    def __init__(self, jsonIn, transactionParam,marketState):
        self.marketState = marketState
        self.buyTimeInSeconds = 0
        self.buyPrice = 0.0
        self.sellPrice = 0.0
        self.sellTimeInSeconds = 0

        self.transactions = []
        self.currencyName = ""
        self.transactionParam = transactionParam
        self.mustSellList = []
        self.keepList = []
        self.__Parse(jsonIn)

        self.lowestTransaction = TransactionBasics.TransactionCountPerSecBase/3
        self.acceptedTransLimit = TransactionBasics.TransactionLimitPerSecBase/3
        self.dataList = []

        tempTransaction = json.loads(jsonIn["transactions"])
        if len(tempTransaction) < 60:
            return
        prices = list(map(lambda x: float(x["p"]), tempTransaction))

        self.peakIndex = prices.index(max(prices))
        self.peakTime = int(tempTransaction[self.peakIndex]["T"])//1000
        self.peakVal = float(tempTransaction[self.peakIndex]["p"])

        self.minPeakIndex = prices.index(min(prices))
        self.minPeakTime = int(tempTransaction[self.minPeakIndex]["T"])//1000
        self.minPeakVal = float(tempTransaction[self.minPeakIndex]["p"])

        self.__DivideDataInSeconds(tempTransaction) #populates the dataList with TransactionData
        self.__AppendToPatternList() # deletes dataList and populates mustBuyList, patternList badPatternList

    def __Parse(self, jsonIn):

        epoch = datetime.utcfromtimestamp(0)
        self.buyPrice = float(jsonIn["buyPrice"])
        datetime_object = datetime.strptime(jsonIn["buyTime"].split(".")[0], '%Y-%b-%d %H:%M:%S')
        self.buyTimeInSeconds = (datetime_object - epoch).total_seconds()
        self.sellPrice = float(jsonIn["sellPrice"])
        datetime_object = datetime.strptime(jsonIn["sellTime"].split(".")[0], '%Y-%b-%d %H:%M:%S')
        self.sellTimeInSeconds = (datetime_object - epoch).total_seconds()
        self.currencyName = jsonIn["name"]

    def __DivideDataInSeconds(self, jsonIn):
        transactionData = TransactionBasics.TransactionData()
        lastEndTime = 0
        stopMiliSecs = int(jsonIn[-1]["T"])
        for x in range(len(jsonIn)):
            curElement = jsonIn[x]
            curMiliSecs = int(curElement["T"])
            if x == 0:
                lastEndTime = curMiliSecs + self.transactionParam.msec

            if curMiliSecs > lastEndTime:
                copyData = copy.deepcopy(transactionData)
                self.dataList.append(copyData)
                transactionData.Reset()
                transactionData.AddData(curElement)
                transactionData.SetTime(curMiliSecs // 1000)
                lastEndTime += self.transactionParam.msec
                while True:
                    if curMiliSecs > lastEndTime and lastEndTime < stopMiliSecs:
                        lastEndTime += self.transactionParam.msec
                        emptyData = TransactionBasics.TransactionData()
                        emptyData.SetTime(lastEndTime // 1000)
                        emptyData.lastPrice = copyData.lastPrice
                        self.dataList.append(emptyData)
                    else:
                        break
            else:
                transactionData.AddData(curElement)
        copyData = copy.deepcopy(transactionData)
        self.dataList.append(copyData)

    def __AppendToPatternList(self):
        lenArray = len(self.dataList)
        if lenArray == 0:
            return
        # print(lenArray, self.dataList)
        for x in range(0, lenArray):
            self.__AppendToPatternListImpl(self.transactionParam.gramCount, x, lenArray)
        del self.dataList

    def __AppendToPatternListImpl(self, ngramCount, curIndex, lenArray):
        totalCount = TransactionBasics.GetTotalPatternCount(ngramCount)
        startBin = curIndex + 1 - totalCount
        endBin = curIndex + 1
        if startBin < 0 or endBin > lenArray:
            return

        lastTotalTradePower = self.dataList[curIndex].totalBuy + self.dataList[curIndex].totalSell
        if lastTotalTradePower < self.acceptedTransLimit:
            return

        pattern = TransactionBasics.TransactionPattern()
        copyList = copy.deepcopy(self.dataList[startBin:endBin])
        dataRange = TransactionBasics.ReduceToNGrams(copyList, ngramCount)
        pattern.AppendWithOutPeaks( dataRange, self.marketState, self.buyPrice, self.buyTimeInSeconds)

        if self.__GetCategory(curIndex) == 1:
            self.mustSellList.append(copy.deepcopy(pattern))
        elif self.__GetCategory(curIndex) == 2:
            self.keepList.append(copy.deepcopy(pattern))

    def __GetCategory(self, curIndex):
        price = self.dataList[curIndex].maxPrice
        time = self.dataList[curIndex].timeInSecs

        if price > self.peakVal * 0.997:
            return 1  # Sell
        elif time > self.peakTime and price > self.peakVal * 0.995:
            return 1  # Sell
        elif time > self.buyTimeInSeconds and time < self.peakTime:
            return 2
        return -1

class BuyAnalyzeMerger:
    def __init__(self, transactionParam, marketState):
        self.mustSellList = []
        self.keepList = []

        self.handlerList = []
        self.peakHelperList = []
        self.transactionParam = transactionParam
        self.marketState = marketState

    def AddFile(self, jsonIn):
        for index in range(len(jsonIn)):
            if not jsonIn[index]:
                continue

            jsonPeakTrans = jsonIn[index]
            handler = BuySellHandler(jsonPeakTrans,self.transactionParam,self.marketState)
            self.handlerList.append(handler)

    def Finalize(self):
        for peak in self.handlerList:
            self.__MergeInTransactions(peak)
            del peak

    def toSellTransactions(self):
        mustSellCount = len(self.mustSellList)
        keepCount = len(self.keepList)
        #self.Print()
        print("Must sell count: ", mustSellCount, " Keep count: ", keepCount)

        allData = np.concatenate( (self.mustSellList, self.keepList), axis=0)
        print("Must sell count: ", mustSellCount, " Keep count: ", keepCount)
        return allData

    def toSellResultsNumpy(self):
        mustSellCount = len(self.mustSellList)
        keepCount = len(self.keepList)

        print("Must sell count: ", mustSellCount, " Keep count: ", keepCount)
        mustSellResult = [1] * mustSellCount
        keepResult  = [0] * keepCount
        returnPatternList = mustSellResult + keepResult
        return returnPatternList

    def Print(self):

        #mustBuyList = np.array(self.mustBuyList)
        buyList = np.array(self.mustSellList)
        badList = np.array(self.keepList)

        for i in range(len(self.patternList[0])):
            a = {'Good': buyList[:, -(i + 1)],
                 'Bad': badList[:, -(i + 1)]}
            #df = pd.DataFrame.from_dict( a, orient='index')
            #df = df.transpose()
            #df.plot.box()
            #mustBuyLegend = str(np.quantile(mustBuyList[:, -(i + 1)], 0.1)) + "," + str(np.quantile(mustBuyList[:, -(i + 1)], 0.5)) + "," + str(np.quantile(mustBuyList[:, -(i + 1)], 0.9))
            buyLegend = str(np.quantile(buyList[:, -(i + 1)], 0.1)) + "," + str(np.quantile(buyList[:, -(i + 1)], 0.25)) + "," \
                        + str(np.quantile(buyList[:, -(i + 1)], 0.5)) + "," + str(np.quantile(buyList[:, -(i + 1)], 0.75)) + "," + str(np.quantile(buyList[:, -(i + 1)], 0.9))
            badLegend = str(np.quantile(badList[:, -(i + 1)], 0.1)) + "," + str(np.quantile(badList[:, -(i + 1)], 0.25)) +\
                        "," + str(np.quantile(badList[:, -(i + 1)], 0.5)) + "," + str(np.quantile(badList[:, -(i + 1)], 0.75)) + "," + str(np.quantile(badList[:, -(i + 1)], 0.9))
            print(str(self.transactionParam.msec) ,"_" , str(i), "_" , buyLegend , " ", badLegend)
            #plt.savefig('Plots/' + str(self.transactionParam.msec) + "_" + str(i) + "_box.pdf")
            #plt.cla()
            #plt.clf()

        #plt.close()

    def __MergeInTransactions(self, handler):

        for pattern in handler.mustSellList:
            self.mustSellList.append(pattern.GetFeatures())

        for pattern in handler.keepList:
            self.keepList.append(pattern.GetFeatures())

class BuyAnalyzeManager:

    def __init__(self, transactionParamList, marketStateIn):
        self.marketState = marketStateIn
        self.transParamList = transactionParamList
        self.buySellMergerList = []
        self.CreateBuyAnalyzeMergers()
        print(self.buySellMergerList)
        self.BuyAnalyzeMergers()
        self.FinalizeMergers()

    def BuyAnalyzeMergers(self):
        jumpDataFolderPath = os.path.abspath(os.getcwd()) + "/Data/BuySellData/"
        onlyJumpFiles = [f for f in listdir(jumpDataFolderPath) if isfile(join(jumpDataFolderPath, f))]
        for fileName in onlyJumpFiles:
            print("Reading Buy", jumpDataFolderPath + fileName, " ")
            file = open(jumpDataFolderPath + fileName, "r")
            try:
                jsonDictionary = json.load(file)
                for merger in self.buySellMergerList:
                    merger.AddFile(jsonDictionary)
            except:
                print("There was a exception in ", fileName)

    def toSellTransactions(self, index):
        return self.buySellMergerList[index].toSellTransactions()

    def toSellResultsNumpy(self, index):
        return self.buySellMergerList[index].toSellResultsNumpy()

    def FinalizeMergers(self):
        for transactionIndex in range(len(self.transParamList)):
            self.buySellMergerList[transactionIndex].Finalize()

    def CreateBuyAnalyzeMergers(self):
        for transactionIndex in range(len(self.transParamList)):
            newMerger = BuyAnalyzeMerger(self.transParamList[transactionIndex], self.marketState)
            self.buySellMergerList.append(newMerger)


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

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('telepathy', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='thread',
            name='placement',
            field=models.TextField(default=None, null=True),
            preserve_default=True,
        ),
    ]


import flask
from flask import Blueprint, render_template, redirect, url_for, request, jsonify
from flask_login import (current_user, login_required)
from flask_paginate import Pagination, get_page_args
from redis import RedisError
from datetime import datetime
import time
import urllib.parse
from redis.commands.search.query import Query
from .document import Document, Version, CurrentVersion
from src.common.config import CFG_VSS_WITH_LUA
from pydantic import ValidationError
from redis_om import NotFoundError

from src.common.utils import get_db, parse_query_string, get_analytics, pretty_title, track_request, requires_access_level, Role

document_bp = Blueprint('document_bp', __name__,
                        template_folder='./templates')

@document_bp.before_request
def before_request():
    # Track the request in a Redis Stream
    track_request()

    # Store visits in a time series visited pages
    if current_user.is_authenticated and request.endpoint == "document_bp.doc":
        get_db().ts().add("keybase:visits", "*", 1, duplicate_policy='first')

@document_bp.route('/autocomplete', methods=['GET'])
@login_required
def autocomplete():
    # Sanitize input for RediSearch
    query = parse_query_string(flask.request.args.get('q'))
    query = "@currentversion_name_fts|currentversion_content_fts:'" + query + "'"
    rs = get_db().ft("document_idx")\
                 .search(Query(query + " @state:{published|review}")
                 .return_field("currentversion_name")
                 .sort_by("creation", asc=False)
                 .paging(0, 10))

    results = []

    for doc in rs.docs:
        results.append({'value': urllib.parse.unquote(doc.currentversion_name),
                        'label': urllib.parse.unquote(doc.currentversion_name),
                        'pretty': pretty_title(urllib.parse.unquote(doc.currentversion_name)),
                        'id': doc.id.split(':')[-1]})

    return jsonify(matching_results=results)

@document_bp.route('/kb-admin', methods=['GET', 'POST'])
@login_required
def browse():
    title = "List documents"
    desc = "Listing documents"
    keys = []
    names = []
    pretty = []
    creations = []
    keydocument = None
    pagination = None
    rs = None
    category = ""
    asc = 0

    try:
        if flask.request.method == 'GET':
            catfilter, tagfilter, queryfilter, prvfilter, sortbyfilter = "", "", "", "", False

            # Check the ordering
            if flask.request.args.get('asc') == "true":
                sortbyfilter = True
                asc = 1

            # Sanitized input for RediSearch
            if flask.request.args.get('q'):
                queryfilter = parse_query_string(flask.request.args.get('q'))
                queryfilter = "@currentversion_name_fts|currentversion_content_fts:'" + queryfilter + "'"
            # If the category is good, can be processed and set in the UI
            if flask.request.args.get('cat'):
                if get_db().hexists("keybase:categories", flask.request.args.get('cat')):
                    catfilter = " @category:{"+flask.request.args.get('cat')+"} "
                    category = flask.request.args.get('cat')

            # Sanitized tags for RediSearch: may be empty afterwards, a search like @tags:{""} fails
            if flask.request.args.get('tag'):
                tag = flask.request.args.get('tag').translate(str.maketrans('', '', "\"@!{}()|-=>"))
                tagfilter = " @tags:{" + tag + "} "

            prv = flask.request.args.get('prv')
            if prv and (prv=='internal' or prv=='public'):
                prvfilter = " @privacy:{" + prv + "} "

            page, per_page, offset = get_page_args(page_parameter='page', per_page_parameter='per_page')
            rs = get_db().ft("document_idx").search(
                Query(queryfilter + catfilter + tagfilter + prvfilter + " @state:{published|review}")
                .return_field("currentversion_name")
                .return_field("creation")
                .sort_by("creation", asc=sortbyfilter)
                .paging(offset, per_page))

            pagination = Pagination(page=page, per_page=per_page, total=rs.total, css_framework='bulma',
                                    bulma_style='small', prev_label='Previous', next_label='Next page')

        # If after sanitizing the input there is nothing to show, redirect to main
        if (rs is not None) and len(rs.docs):
            for key in rs.docs:
                keys.append(key.id.split(':')[-1])
                names.append(urllib.parse.unquote(key.currentversion_name))
                pretty.append(pretty_title(urllib.parse.unquote(key.currentversion_name)))
                creations.append(datetime.utcfromtimestamp(int(key.creation)).strftime('%Y-%m-%d'))
            keydocument = zip(keys, names, pretty, creations)

        # Get the categories
        categories = get_db().hgetall("keybase:categories")
        return render_template('browse.html', title=title, desc=desc, categories=categories, keydocument=keydocument, page=page,
                               per_page=per_page, pagination=pagination, category=category, asc=asc, privacy=prv)
    except RedisError as err:
        return redirect(url_for("document_bp.browse"))

@document_bp.route('/save', methods=['POST'])
@login_required
@requires_access_level(Role.EDITOR)
def save():
    unixtime = int(time.time())

    try:
        currentversion = CurrentVersion(
            name=urllib.parse.unquote(request.form['name']),
            content=urllib.parse.unquote(request.form['content']),
            last=unixtime,
            owner=current_user.id
        )

        editorversion = Version(
            name=urllib.parse.unquote(request.form['name']),
            content=urllib.parse.unquote(request.form['content']),
            last=unixtime,
            owner=current_user.id
        )

        doc = Document(
            editorversion=editorversion,
            currentversion=currentversion,
            description="",
            keyword="",
            tags="",
            creation=unixtime,
            updated=unixtime,
            processable=0,
            author=current_user.id,
            versions=[]
        )

        doc.save()
    except ValidationError as e:
        print(e)

    return jsonify(message="Document created", id=doc.pk)

@document_bp.route('/publish', methods=['POST'])
@login_required
@requires_access_level(Role.ADMIN)
def publish():
    try:
        document = Document.get(request.form['id'])
    except NotFoundError:
        return jsonify(message="Error publishing the document"), 404

    if document.state == "published":
        return jsonify(message="Document already published"), 403

    unixtime = int(time.time())

    version = Version(
        name=urllib.parse.unquote(request.form['name']),
        content=urllib.parse.unquote(request.form['content']),
        last=unixtime,
        owner=document.editorversion.owner
    )

    currentversion = CurrentVersion(
        name=urllib.parse.unquote(request.form['name']),
        content=urllib.parse.unquote(request.form['content']),
        last=unixtime,
        owner=document.editorversion.owner
    )

    document.versions.insert(0, version)

    # Save the document and change the state TAG to "published"
    document.editorversion = version
    document.currentversion = currentversion
    document.state = "published"
    document.processable = 1
    document.updated = unixtime
    document.save()

    data = {'type': 'publish',
            'id': request.form['id']}
    get_db().xadd("keybase:events", data)

    return jsonify(message="Document published")

@document_bp.route('/addmetadata', methods=['POST'])
@login_required
@requires_access_level(Role.EDITOR)
def addmetadata():
    try:
        document = Document.get(request.form['id'])
    except NotFoundError:
        return jsonify(message="The document does not exist", code="error"), 404

    if len(request.form['keyword']) > 160:
        return jsonify(message="Keywords too long: max is 160 chars", code="error"), 500

    if len(request.form['description']) > 160:
        return jsonify(message="Description too long: max is 160 chars", code="error"), 500

    document.keyword = request.form['keyword']
    document.description = request.form['description']
    document.save()
    return jsonify(message="The metadata has been saved", code="success"), 200

@document_bp.route('/addtag', methods=['POST'])
@login_required
@requires_access_level(Role.EDITOR)
def addtag():
    taglist = []

    try:
        document = Document.get(request.form['id'])
    except NotFoundError:
        return jsonify(message="The document does not exist", code="error"), 404

    # Make sure the tag exists and is valid
    if not get_db().hexists("keybase:tags", request.form['tag']):
        return jsonify(message="The tag does not exist", code="error")

    # Get tags for this document
    if (document.tags is not None) and (len(document.tags) > 0):
        taglist = document.tags.split('|')

    # The document hasn't the tag, and it can be added
    if not taglist.count(request.form['tag']) > 0:
        taglist.append(request.form['tag'])
        document.tags = "|".join(taglist)
        document.save()
    else:
        return jsonify(message="Document already tagged", code="warn")

    return jsonify(message="The tag has been added", code="success", tags=taglist)

@document_bp.route('/addcategory', methods=['POST'])
@login_required
@requires_access_level(Role.EDITOR)
def addcategory():
    try:
        document = Document.get(request.form['id'])
    except NotFoundError:
        return jsonify(message="The document does not exist", code="error"), 404

    # Make sure the category exists
    if not get_db().hexists("keybase:categories", request.form['cat']) and len(request.form['cat']):
        return jsonify(message="The category does not exist", code="error")

    document.category = request.form['cat']
    document.save()
    return jsonify(message="The category has been changed", code="success")

@document_bp.route('/setprivacy', methods=['POST'])
@login_required
@requires_access_level(Role.ADMIN)
def setprivacy():
    try:
        document = Document.get(request.form['id'])
    except NotFoundError:
        return jsonify(message="The document does not exist", code="error"), 404

    # Make sure the privacy is correct
    if not (request.form['privacy'] == 'internal') and not (request.form['privacy'] == 'public'):
        return jsonify(message="The privacy setting not exist", code="error"), 500

    # Do not recommend
    privacy = {"privacy": request.form['privacy']}
    get_db().hset("keybase:vss:{}".format(request.form['id']), mapping=privacy)

    document.privacy = request.form['privacy']
    document.save()
    return jsonify(message="The privacy has been changed", code="success")

@document_bp.route('/deltag', methods=['POST'])
@login_required
@requires_access_level(Role.EDITOR)
def deltag():
    taglist = []

    try:
        document = Document.get(request.form['id'])
    except NotFoundError:
        return jsonify(message="The document does not exist", code="error"), 404

    # Get tags for this document
    if (document.tags is not None) and (len(document.tags) > 0):
        taglist = document.tags.split('|')
        taglist.remove(request.form['tag'])
        document.tags = "|".join(taglist)
        document.save()

    return jsonify(message="The tag has been removed", code="success", tags=taglist)

@document_bp.route('/update', methods=['POST'])
@login_required
@requires_access_level(Role.EDITOR)
def update():
    try:
        document = Document.get(request.form['id'])
    except NotFoundError:
        return jsonify(message="Error saving the document"), 404

    unixtime = int(time.time())

    # If the document has never been published, on saving, it should not become a review
    if document.state != "draft":
        document.state = "review"

    # Save the document, which becomes a current review
    document.editorversion.content = urllib.parse.unquote(request.form['content'])
    document.editorversion.name = urllib.parse.unquote(request.form['name'])
    document.editorversion.last = unixtime
    document.editorversion.owner = current_user.id
    document.save()

    return jsonify(message="Document saved as {}".format(document.state))

@document_bp.route('/edit/<pk>')
@login_required
@requires_access_level(Role.EDITOR)
def edit(pk):
    title = "Edit Document"
    desc = "Edit Document"

    try:
        document = Document.get(pk)
    except NotFoundError:
        return redirect(url_for('document_bp.browse')), 404

    if document.state == 'review' and document.editorversion.owner != current_user.id and not current_user.is_admin():
        return render_template('locked.html', name=document.currentversion.name), 403

    # These are all the categories in the system, for the taxonomy
    # System tags are not returned, now. They can be searched
    categories = get_db().hgetall("keybase:categories")

    document.editorversion.name = urllib.parse.quote(document.editorversion.name)
    document.editorversion.content = urllib.parse.quote(document.editorversion.content)

    return render_template('edit.html',
                           title=title,
                           desc=desc,
                           document=document,
                           categories=categories,
                           pretty=pretty_title(urllib.parse.unquote(document.editorversion.name)))

@document_bp.route('/delete/<pk>')
@login_required
@requires_access_level(Role.ADMIN)
def delete(pk):
    try:
        Document.delete(pk)
        get_db().delete("keybase:vss:{}".format(pk))
    except NotFoundError:
        return redirect(url_for('document_bp.browse')), 404

    return redirect(url_for('document_bp.browse')), 302

@document_bp.route('/doc/<pk>', defaults={'prettyurl': None})
@document_bp.route('/doc/<pk>/<prettyurl>')
@login_required
def doc(pk, prettyurl):
    title = "Read Document"
    desc = "Read Document"
    keys = []
    names = []
    pretty = []
    suggestlist = None

    try:
        document = Document.get(pk)
    except NotFoundError:
        return render_template('404.html'), 404

    # Check if the document is bookmarked
    bookmarked = get_db().hexists("keybase:bookmark:{}".format(current_user.id), pk)

    # If it is a draft, make sure the user is not a viewer
    if document.state == 'draft' and current_user.is_viewer():
        return render_template('locked.html', name=document.currentversion.name), 403

    # If it is a draft, make sure the user is the author
    # If it is a draft, role is admin: can see, edit and publish
    if document.state == 'draft' and document.author != current_user.id and not current_user.is_admin():
        return render_template('locked.html', name=document.currentversion.name), 403

    document.currentversion.name = urllib.parse.quote(document.currentversion.name)
    document.currentversion.content = urllib.parse.quote(document.currentversion.content)

    # The document can be rendered, count the visit
    get_db().ts().add("keybase:docview:{}".format(pk), "*", 1, duplicate_policy='first')

    # Only the admin can see document visits
    analytics = None
    if current_user.is_admin():
        analytics = get_analytics("keybase:docview:{}".format(pk), 86400000, 2592000000)

    # Fetch recommendations using LUA and avoid sending vector embeddings back and forth
    # The first element in the returned list is the number of keys returned, start iterator from [1:]
    # Then, iterate the results in pairs, because the key name is alternated with the returned fields
    if get_db().hexists("keybase:vss:{}".format(pk), "content_embedding"):
        if CFG_VSS_WITH_LUA:
            keys_and_args = ["keybase:vss:{}".format(pk)]
            res = get_db().eval(
                "local vector = redis.call('HMGET',KEYS[1], 'content_embedding') local searchres = redis.call('FT.SEARCH','vss_idx','(@state:{published|review})=>[KNN 6 @content_embedding $B AS score]','PARAMS','2','B',vector[1], 'SORTBY', 'score', 'ASC', 'LIMIT', 1, 6,'RETURN',2,'score','name','DIALECT',2) return searchres",
                1, *keys_and_args)
            it = iter(res[1:])
            for x in it:
                keys.append(str(x.split(':')[-1]))
                docname = str(next(it)[3])
                names.append(docname)
                pretty.append(pretty_title(docname))
            suggestlist = zip(keys, names, pretty)
        else:
            embedding = get_db(decode=False).hget("keybase:vss:{}".format(pk), "content_embedding")
            q = Query("(@state:{published|review})=>[KNN 6 @content_embedding $B AS score]")\
                .return_field("score")\
                .return_field("name")\
                .sort_by("score", asc=True)\
                .dialect(2)\
                .paging(1, 6)
            res = get_db().ft("vss_idx").search(q, query_params={"B": embedding})
            it = iter(res.docs[0:])
            for x in it:
                keys.append(str(x['id'].split(':')[-1]))
                names.append(str(x['name']))
                pretty.append(pretty_title(str(x['name'])))
            suggestlist = zip(keys, names, pretty)

    return render_template('view.html',
                           title=title,
                           desc=desc,
                           docid=pk,
                           bookmarked=bookmarked,
                           document=document,
                           suggestlist=suggestlist,
                           analytics=analytics)

@document_bp.route('/new/<doc>')
@login_required
@requires_access_level(Role.EDITOR)
def new(doc):
    title = "New Document"
    desc = "New Document"
    template = ""

    if doc == 'case':
        template = urllib.parse.quote(
            "## Applies to:\n\n\n
\n## Executive Summary \n\n\n
\n
\n
\n## Introduction \n\n\n
\n
\n
\n## Analysis \n\n\n
\n
\n
\n## Alternatives and Decision Criteria \n\n\n
\n
\n
\n## Recommendations and Implementation Plan \n\n\n
\n
\n
\n## Conclusion \n\n\n
\n
\n
\n## References")
    elif doc == 'troubleshooting':
        template = urllib.parse.quote(
            "## Applies to:\n\n\n
\n## Symptoms \n\n\n
\n
\n
\n## Changes \n\n\n
\n
\n
\n## Cause \n\n\n
\n
\n
\n## Solution \n\n\n
\n
\n
\n## References")
    elif doc == 'design':
        template = urllib.parse.quote(
            "## Applies to:\n\n\n
\n## Purpose \n\n\n
\n
\n
\n## Scope \n\n\n
\n
\n
\n## Details \n\n\n
\n
\n
\n## References")
    elif doc == 'howto':
        template = urllib.parse.quote(
            "## Applies to:\n\n\n
\n## Goal \n\n\n
\n
\n
\n## Solution \n\n\n
\n
\n
\n## References")
    elif doc == 'qa':
        template = urllib.parse.quote(
            "## Applies to:\n\n\n
\n## Question \n\n\n
\n
\n
\n## Answer \n\n\n
\n
\n
\n## References")
    return render_template('new.html', title=title, desc=desc, template=template)


from datetime import datetime
from abc import abstractmethod
import os
from sqlalchemy import Column, Text, Integer
from base import Base

class AudioFile(Base):
    """Represent a song played on a music player

    Author: Jaguar Perlas
    ID: A01175812
    """

    __tablename__ = "collection"
    id = Column(Integer, primary_key=True)
    title = Column(Text, nullable=True)
    artist = Column(Text, nullable=True)
    # album = Column(Text, nullable=True)
    runtime = Column(Text, nullable=False)
    # genre = Column(Text, nullable=True)
    rating = Column(Text, nullable=True)
    pathname = Column(Text, nullable=True)
    date_added = Column(Text, nullable=True)
    last_played = Column(Text, nullable=True)

    def __init__(self, title: str, artist: str, runtime: str, rating: int = 0, pathname: str = None) -> None:
        """Initialize a song with a title, artist, album, date added, runtime, pathname, filename,
        user rating, last played, and play count."""
        if type(self) == AudioFile:
            raise ValueError('Creation of super class not allowed')
        self.title = title
        self.artist = artist
        self.runtime = runtime
        self.rating = rating
        self.pathname = pathname
        self.filename = os.path.basename(pathname)
        self.user_rating = rating

        self.date_added = datetime.now()
        self.play_count = 0
        self.last_played = None

        # self._usage = UsageStats(datetime.now())
        AudioFile._validate_constructor(title, artist, runtime, rating, self.pathname)

    @staticmethod
    def _validate_constructor(title: str, artist: str, runtime: str, rating: int, pathname) -> None:
        """Validate the values passed into the constructor"""
        if not ((type(title) != str) or (type(artist) != str) or (type(runtime) != str) or
                (type(rating) != int)):
            if not os.path.exists(pathname):
                raise ValueError('Path does not exist.')
            pass
        else:
            raise ValueError('Values must be a string and "rating" must be an int.')

    @abstractmethod
    def get_description(self):
        """Return a string containing the description of the song"""
        song_description = '%s by %s from the album '' added on %s. Runtime is %s. Last' \
                           ' played on %s. User rating is %s' % (self.title, self._artist,
                                                                 self.date_added, self.runtime,
                                                                 self._last_played, self.user_rating)
        pass

    def song_location(self) -> str:
        """Return a string containing the path of the song file"""
        song_location = '%s is located at %s' % (self.filename, self.pathname)
        return song_location

    def play_song(self) -> None:
        """Plays the song"""
        self.increment_usage_stats()
        print('Now playing: %s by %s' % (self.title, self._artist))
        print('Play count is now %d' % self.play_count)

    @property
    def _user_rating(self) -> int:
        """Gets and returns the user rating"""
        return self.rating

    @_user_rating.setter
    def _user_rating(self, user_rating: int) -> None:
        """Sets the user rating to desired value"""
        if type(user_rating) != int:
            raise ValueError('Input must be a number')
        else:
            if (user_rating < 0) or (user_rating > 5):
                raise ValueError('Rating must be a number out of 10')
            else:
                self.user_rating = int(user_rating)

    @abstractmethod
    def meta_data(self):
        song_dict = {'title': self._title, 'artist': self._artist,
                     'date_added': self.date_added, 'runtime': self.runtime,
                     'pathname': self.pathname, 'filename': self.filename, 'play_count':
                         self.play_count, 'last_played': self._last_played,
                     'rating': self.user_rating}
        pass

    #UsageStats functions below vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

    @property
    def _date_added(self):
        """ return the date the song or playlist was added to the library """
        return self._date_added.strftime("%Y-%m-%d")

    @property
    def _last_played(self):
        """ return the date the song or playlist was last played """
        if self.last_played is None:
            return None
        else:
            return self.last_played.strftime("%Y-%m-%d")

    @property
    def _play_count(self):
        """ return the number of times the song or playlist has been played """
        return self._play_count

    def increment_usage_stats(self):
        """ update the play count and last played time when a song is played """
        self.play_count += 1
        self.last_played = datetime.now()

    @classmethod
    def __valid_datetime(cls, date):
        """ private method to validate the date is datetime object """
        if type(date) is not datetime:
            return False
        else:
            return True