class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_word = False

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word):
        cur = self.root

        for letter in word:
            if letter not in cur.children:
                cur.children[letter] = TrieNode()
            cur = cur.children[letter]

        cur.is_word = True

    def exists(self, word):
        cur = self.root

        for letter in word:
            if letter not in cur.children:
                return False
            cur = cur.children[letter]

        return cur.is_word

class Solution:
    def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
        '''
        Implement trie and make a trie from every word in words.

        For each cell in the board, if the letter exists in the top level of the trie, we traverse to see if words exist.

        Traverse the trie with a dfs, but only advance if children are adjacent to your current cell.
        Use backtracking (set a cell as used, recurse, then unset) to search the board without repeating cells.
        We are traversing the trie and the board at the same time, but only exploring board paths that exist in the trie.
        If the current TrieNode is a word, it must go in results, because we only explore word paths from the wordTrie.

        For complexity, call "c" the sum of word lengths in words.
        Call "m" the board width and "n" the board height.
        Call "L" the length of the longest word in words.

        Building trie: O(number of total characters in words), aka O(c) time and space

        Traversing from one cell: worst case for longest word, we start with 4 direction options then have 3 at each step. This comes out to O(4*3^(L-1)). O(mn) space for recursion stack.

        Traversing all cells: O(mn * 4 * 3^(L-1)) time, O(mn) space based on the space for traversal from one cell.

        The time for traversing the cells overwhelms the time to build the trie, based on the constraint numbers in the description.

        Time: O(mn * 4 * 3^(L-1))
        Space: O(c) + O(mn)
        '''

        USED = '#'
        wordTrie = Trie()

        for word in words:
            wordTrie.insert(word)

        result = set()

        def backtrack(node, prefix, i, j):
            if node.is_word:
                result.add(prefix)

            for di, dj in ((0,1), (0,-1), (-1,0), (1,0)):
                ni, nj = i + di, j + dj
                if 0 <= ni < len(board) and 0 <= nj < len(board[0]) and board[ni][nj] in node.children:
                    tmp = board[ni][nj]
                    board[ni][nj] = USED
                    backtrack(node.children[tmp], prefix + tmp, ni, nj)
                    board[ni][nj] = tmp

        for i in range(len(board)):
            for j in range(len(board[i])):
                if board[i][j] in wordTrie.root.children:
                    tmp = board[i][j]
                    board[i][j] = USED
                    backtrack(wordTrie.root.children[tmp], tmp, i, j)
                    board[i][j] = tmp

        return list(result)


import os
import requests
from lxml import html as html
from rich import print

clear = lambda: os.system('cls')

DEBUG = True
BASE_URL = 'https://www.larepublica.co/'
XPATH_LINK_TO_ARTICLES = '//a[@class="{0}Sect"]/@href'
XPATH_NEWS_TITLES = '//a[@class="{0}Sect"]/text()' # The title showed in the news list
XPATH_NEW_TITLE = '//div[@class="mb-auto"]/h2/span/text()'
XPATH_NEW_ABSTRACT = '//div[@class="lead"]/p/text()'
XPATH_NEW_CONTENT = '//div[@class="html-content"]/p/text()'

def run():
    ''''Ask for the news category to see'''
    clear()
    print('[yellow]NOTICIERO[/yellow] [cyan]COLOMBIANO[/cyan] [red]AL INSTANTE[red]')
    print('[yellow]Qué noticias deseas explorar?[/yellow]')
    print('''
    [1]-> Finanzas
    [2]-> Economía
    [3]-> Empresas
    [4]-> Ocio
    [5]-> Globoeconomía
    [6]-> Caja fuerte
    ''')
    option = input('')

    url = BASE_URL
    section = ''

    if option == '1':
        section = 'finanzas'
    elif option == '2':
        section = 'economia'
    elif option == '3':
        section = 'empresas'
    elif option == '4':
        section = 'ocio'
    elif option == '5':
        section = 'globoeconomia'
    elif option == '6':
        section = 'caja-fuerte'
    else:
        print('[red]Opción incorrecta[/red]')
        return

    url += section  # Creating URL to request

    try:
        response = requests.get(url)
        if response.status_code == 200:
            page = response.content.decode('utf-8')
            parsed = html.fromstring(page)

            links_of_news = parsed.xpath(XPATH_LINK_TO_ARTICLES.format(section))
            titles_of_news = parsed.xpath(XPATH_NEWS_TITLES.format(section))

            news = []
            print(len(links_of_news))
            print(len(titles_of_news))
            for i in range(len(links_of_news) ):
                buffer = dict(link=links_of_news[i], title=titles_of_news[i])
                news.append(buffer)
            display_news(news)

        else:
            raise ValueError(f'Error:{response.status_code}')
    except ValueError as ve:
        if DEBUG:
            print(f'[red]{ve}[/red]')
        else:
            print('[red]Ocurrió un error[/red]')

def display_news(news):
    '''Shows all news of the selected category'''
    clear()
    news_to_display = 15
    grouped_news = [news[i:i+news_to_display] for i in range(0,len(news), news_to_display)]
    page = 0
    selected = ''
    absolute_id = 0
    while True:
        print('[yellow]Escoge el número de la noticia que quieras leer[/yellow]')
        print('[yellow]Ingresa "<" o ">" para cambiar de página[/yellow]\n')
        print(f'[cyan]Página {page + 1}[/cyan]\n')
        for new in grouped_news[page]:
            print(f"[{absolute_id + 1}]-> {new['title']}")
            absolute_id += 1
        print('')
        selected = input('')

        if selected == '<':
            clear()
            page -= 1
            if page < 0:
                page = len(grouped_news) - 1
                absolute_id = len(news) - len(grouped_news[page])
            else:
                absolute_id -= (news_to_display + len(grouped_news[page + 1]))
        elif selected == '>':
            clear()
            page += 1
            if page >= len(grouped_news):
                page = 0
                absolute_id = 0
        elif selected.isnumeric():
            selected = int(selected)
            if selected > len(news) or selected <= 0:
                clear()
                print('[red]Noticia escogida fuera de rango[/red]')
            else:
                break
        else:
            print('[red]Opción inválida[/red]')

    show_new(news[selected - 1])

def show_new(new):
    '''Show the title, abstract and content of selected new'''
    clear()
    response = requests.get(new['link'])
    if response.status_code == 200:
        page = response.content.decode('utf-8')
        parsed = html.fromstring(page)

        title = new['title']
        abstract = parsed.xpath(XPATH_NEW_ABSTRACT)
        paragraphs = parsed.xpath(XPATH_NEW_CONTENT)

        full_content = ''
        for p in paragraphs:
            full_content += p + '\n'

        abstract = abstract[0].replace('\n', '')
        full_content = full_content.replace('\n', '\n\n')

        title = title.strip()
        abstract = abstract.strip()
        full_content.strip()

        print(f'[cyan]{title}[/cyan]\n\n')
        print(f'[green]{abstract}[/green]\n')
        print(f'[yellow]{full_content}[/yellow]\n\n')
        answer = ''
        while answer.capitalize() != 'S' and answer.capitalize() != 'N':
            print('[green]Desea ver más noticias?[/green] (S/N)')
            answer = input('')
            answer = answer.capitalize()
            if answer != 'S' and answer != 'N':
                print('[red]Opción incorrecta[/red]')
        if answer == 'S':
            clear()
            run()

    else:
        print(f'[red]Error: {response.status_code}[/red]')

if __name__ == '__main__':
    run()


from src.set1.problem7.problem1_7 import *
from src.set2.problem9.problem2_9 import *

def main():

    # Read input file
    finput = open("1-7tst.txt", "r")

    # Encrypt, print encrypted string
    encrypted = aes_128_ecb_encrypt(finput.read(), "PAUL SIMON ROCKS")
    print("encrypted", encrypted, len(encrypted))
    decrypted = aes_128_ecb_decrypt(encrypted, "PAUL SIMON ROCKS")
    print("decrypted", decrypted)
    print(decrypted)

if __name__ == "__main__":
    main()


import os
import sys
from pychorus import find_and_output_chorus

def extract_all_file(files_path):

    """
    批量提取音乐高潮
    Args:
        files_path (str): 文件夹路径
    """
    # 文件夹路径
    modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
    for filepath in os.listdir(files_path):
        # 路径处理
        datapath = os.path.join(modpath, files_path + filepath)
        # output文件夹是否存在
        targets = f"{modpath}\\output\\"
        if not os.path.exists(targets):
            os.makedirs(targets)
        # 提取音乐高潮至当前output文件夹下
        find_and_output_chorus(
            datapath, f"{targets}{filepath.split('.')[0]}_high.wav", 40
        )


import json
import logging
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

from grubbers.chpic import chpic_stickers_grubber
from grubbers.combot import combot_stickers_grubber
from grubbers.tlgrm import tlgrm_stickers_grubber

def main():
    begin = time.time()
    logger_main = logging.getLogger(__name__)
    logger_main.setLevel(logging.INFO)
    handler = logging.FileHandler(f"logs/main.log", mode='w')
    formatter = logging.Formatter("%(name)s %(asctime)s %(levelname)s %(message)s")
    handler.setFormatter(formatter)
    logger_main.addHandler(handler)

    try:
        with ThreadPoolExecutor(max_workers=3) as executor:
            future_list = []
            result_list = []

            future1 = executor.submit(tlgrm_stickers_grubber)
            future3 = executor.submit(chpic_stickers_grubber)
            future_list.append(future1)
            future_list.append(future3)
            logger_main.info("Запустил процессы скрапинга")

            for f in as_completed(future_list):
                worker_done = f.result()
                result_list = result_list + (f.result())
                logger_main.info(f'отработал {worker_done}')

    except Exception as e:
        logger_main.error(f'Ошибка в main с процессами -  {e}')

        try:
            with open("files/stickers_total.files", "w", encoding="utf-8") as f:
                json.dump(result_list, f, indent=4, ensure_ascii=False)
            logger_main.info("записал список в итоговый файл")
            logger_main.info(
                f'Общее время работы скрипта - {time.time() - begin}. В итоговм списке {len(result_list)} записей.')
        except Exception as eeee:
            logger_main.error(f"Проблеммы с записью в файл stickers_total.files - {eeee}")

#
# if __name__ == "__main__":
#     logging.basicConfig(level=logging.INFO, filename="stickers_log_tlgrm.log", filemode="w",
#                         format="%(asctime)s %(levelname)s %(message)s")
#     main()

q
# def collect_stickers():
#     with open("files/stickers_chpic.files", "r", encoding="utf-8") as file:
#         chpic_list = files.load(file)
#     with open("files/stickers_combot.files", "r", encoding="utf-8") as file:
#         combot_list = files.load(file)
#     with open("files/stickers_tlgrm.files", "r", encoding="utf-8") as file:
#         tlgrm_list = files.load(file)
#
#     result_list = combot_list + chpic_list + tlgrm_list
#     print(len(result_list))
#     return result_list

if __name__ == "__main__":
    main()


"""The in out flux dialog"""
from qtpy.QtWidgets import (QDialog, QHBoxLayout, QLabel,
                            QPushButton, QVBoxLayout, QComboBox)
from cnapy.appdata import AppData

class InOutFluxDialog(QDialog):
    """A dialog to plot in out fluxes on a metabolite"""

    def __init__(self, appdata: AppData):
        QDialog.__init__(self)
        self.setWindowTitle("Compute in/out fluxes")

        self.appdata = appdata

        self.layout = QVBoxLayout()

        t1 = QLabel("Choose metabolite")
        self.layout.addWidget(t1)
        self.metabolite_chooser = QComboBox()
        self.layout.addWidget(self.metabolite_chooser)
        l = QHBoxLayout()
        self.button = QPushButton("Plot")
        self.cancel = QPushButton("Close")
        l.addWidget(self.button)
        l.addWidget(self.cancel)
        self.layout.addItem(l)
        self.setLayout(self.layout)

        # Connecting the signal
        self.cancel.clicked.connect(self.reject)
        self.button.clicked.connect(self.compute)
        self.update()

    def update(self):
        sorted_metabolite_ids = sorted([x.id for x in self.appdata.project.cobra_py_model.metabolites], reverse=True)
        for metabolite_id in sorted_metabolite_ids:
            self.metabolite_chooser.insertItem(0, metabolite_id)

    def compute(self):
        metabolite = self.metabolite_chooser.currentText()
        self.appdata.window.centralWidget().in_out_fluxes(metabolite)


import io
import json
import logging
import pathlib
from datetime import datetime

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from tensorflow.keras import backend as K
from tensorflow.keras import metrics

from src import util_funcs
from src.explore import plot_funcs

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# https://www.tensorflow.org/tensorboard/image_summaries#visualizing_multiple_images
def plot_to_image(figure):
    """Converts the matplotlib plot specified by 'figure' to a PNG image and
    returns it. The supplied figure is closed and inaccessible after this call."""
    # Save the plot to a PNG in memory.
    buf = io.BytesIO()
    plt.savefig(buf, format='png')
    # Closing the figure prevents it from being displayed directly inside
    # the notebook.
    plt.close(figure)
    buf.seek(0)
    # Convert PNG buffer to TF image
    image = tf.image.decode_png(buf.getvalue(), channels=4)
    # Add the batch dimension
    image = tf.expand_dims(image, 0)
    return image

def mse_sum(y_true, y_pred):
    # sum over dimensions
    rl = K.sum(K.square(y_pred - y_true), axis=-1)
    # take mean over samples
    return K.mean(rl)

class Trainer():

    def __init__(self,
                 model=None,
                 X_samples=None,
                 labels=None,
                 distances=None,
                 logs_path=None,
                 epochs=1,
                 batch=256,
                 lr=1e-3,
                 clip_norm=1.0,
                 best_loss=False,
                 save_path: pathlib.Path = None,
                 test_indices: np.ndarray = None,
                 shuffle_seed: int = 0):
        self.model = model
        self.labels = labels
        self.distances = distances
        self.epochs = epochs
        self.batch = batch
        self.best_loss = best_loss
        self.train_loss = metrics.Mean(name='train_loss', dtype='float32')
        self.val_loss = metrics.Mean(name='val_loss', dtype='float32')
        self.history = {}
        # optimizer
        self.learning_rate = lr
        self.optimizer = tf.keras.optimizers.Adam(learning_rate=lr, clipnorm=clip_norm)
        # logging
        self.writer = None
        if logs_path is not None:
            path = f'{datetime.now().strftime("%Hh%Mm%Ss")}_{model.theme}_e{epochs}_b{batch}_lr{lr}'
            path = str(logs_path / f'{path}')
            logger.info(f'Tensorboard log directory: {path}')
            self.writer = tf.summary.create_file_writer(path)
        # saving
        self.save_path = None
        self.save_path_history = None
        if save_path is not None:
            if not save_path.exists():
                save_path.mkdir(exist_ok=True, parents=True)
            self.save_path = str(pathlib.Path(save_path / 'weights'))
            self.save_path_history = str(pathlib.Path(save_path / 'history.json'))
        # setup datasets
        self.X_train = X_samples[~test_indices]
        self.X_val = X_samples[test_indices]
        # training dataset
        training_dataset = tf.data.Dataset.from_tensor_slices(self.X_train)
        training_dataset = training_dataset.shuffle(buffer_size=self.X_train.shape[0],
                                                    reshuffle_each_iteration=True,
                                                    seed=shuffle_seed)
        self.training_dataset = training_dataset.batch(batch, drop_remainder=False)
        # validation dataset
        validation_dataset = tf.data.Dataset.from_tensor_slices(self.X_val)
        validation_dataset = validation_dataset.shuffle(buffer_size=self.X_val.shape[0],
                                                        reshuffle_each_iteration=False,
                                                        seed=shuffle_seed)
        self.validation_dataset = validation_dataset.batch(batch, drop_remainder=False)

    @tf.function
    def training_step(self, batch_x, training):
        with tf.GradientTape() as tape:
            self.model(batch_x, training=training)
            loss = sum(self.model.losses)
        # Update the weights of the VAE.
        grads = tape.gradient(loss, self.model.trainable_weights)
        # guards against exploding gradients
        # grads, global_norm = tf.clip_by_global_norm(grads, self.max_global_norm)
        self.optimizer.apply_gradients(zip(grads, self.model.trainable_weights))
        if training:
            self.train_loss(loss)
        else:
            self.val_loss(loss)

    def train(self, crank=None):
        # process epochs
        least_loss = np.inf
        best_epoch = 0
        batch_counter = 0  # track aggregate counter over different batches
        n_iters = self.epochs
        if crank:
            n_iters = crank
        for epoch_step in range(n_iters):
            logger.info(f'Epoch: {epoch_step + 1}')
            # setup progress bar
            progress_bar = tf.keras.utils.Progbar(self.X_train.shape[0])
            # run custom epoch setup steps
            self.epoch_setup(epoch_step)
            # reset metrics states
            self.train_loss.reset_states()
            self.reset_summary_metrics()
            # run batches
            for batch_step, training_batch in enumerate(
                    self.training_dataset.as_numpy_iterator()):
                # iterate
                self.training_step(training_batch, training=True)
                if not np.isfinite(self.train_loss.result()):
                    logger.warning(f'Invalid loss encountered on batch step {batch_step}')
                    break
                # custom batch operations
                if batch_step % 25 == 0:
                    # process batch level tensorboard
                    self.batch_writes(batch_counter)
                    self.reset_summary_metrics()
                # progress
                progress_bar.add(self.batch, values=[('loss', self.train_loss.result())])
                batch_counter += 1
            # epoch steps
            if not np.isfinite(self.train_loss.result()):
                break
            # reset metrics states
            self.val_loss.reset_states()
            self.reset_summary_metrics()
            # compute validation loss
            for validation_batch in self.validation_dataset.as_numpy_iterator():
                self.training_step(validation_batch, training=False)
            if not np.isfinite(self.val_loss.result()):
                logger.warning(
                    f'Step: {epoch_step + 1}: non finite validation loss encountered: '
                    f'{self.val_loss.result()}')
            else:
                logger.info(
                    f'Step: {epoch_step + 1}: validation loss: {self.val_loss.result()}')
                # process epoch level tensorboard
                self.epoch_writes(epoch_step)
            # write validation history
            self.update_history()
            # save if best weights
            if self.save_path is not None and self.best_loss and self.val_loss.result() < least_loss:
                logger.info(f'Updating least loss - saving to {self.save_path}')
                least_loss = self.val_loss.result()
                best_epoch = epoch_step + 1
                self.model.save_weights(self.save_path, overwrite=True)
        # finalise weights
        if not np.isfinite(self.train_loss.result()):
            logger.error(f'Invalid loss encountered: {self.train_loss.result()}')
        elif self.save_path is not None:
            if self.best_loss:
                logger.info(f'Best loss {least_loss:.2f} from epoch step: {best_epoch}.')
            else:
                logger.info('Saving last weights')
                self.model.save_weights(self.save_path, overwrite=True)
            # save history
            with open(self.save_path_history, 'w') as json_out:
                json.dump(self.history, json_out)

    def reset_summary_metrics(self):
        if hasattr(self.model, 'summary_metrics'):
            for metric_val in self.model.summary_metrics.values():
                metric_val.reset_states()

    def update_history(self):
        if 'val_loss' not in self.history:
            self.history['train_loss'] = [float(self.train_loss.result())]
            self.history['val_loss'] = [float(self.val_loss.result())]
        else:
            self.history['train_loss'].append(float(self.train_loss.result()))
            self.history['val_loss'].append(float(self.val_loss.result()))
        # add metrics - this should be based on validation step
        if hasattr(self.model, 'summary_metrics'):
            for metric_name, metric_val in self.model.summary_metrics.items():
                target_name = f'val_{metric_name}'
                if target_name not in self.history:
                    self.history[target_name] = [float(metric_val.result().numpy())]
                else:
                    self.history[target_name].append(float(metric_val.result().numpy()))

    def batch_writes(self, batch_step):
        # log scalars
        if self.writer is not None:
            with self.writer.as_default():
                tf.summary.scalar('training loss', self.train_loss.result(), step=batch_step)
                if hasattr(self.model, 'summary_metrics'):
                    for metric_name, metric_val in self.model.summary_metrics.items():
                        tf.summary.scalar(metric_name, metric_val.result(), step=batch_step)

    def epoch_setup(self, epoch_step):
        pass

    def epoch_writes(self, epoch_step):
        if self.writer is not None:
            with self.writer.as_default():
                # write validation loss
                tf.summary.scalar('validation loss', self.val_loss.result(), step=epoch_step)
                if hasattr(self.model, 'summary_metrics'):
                    for metric_name, metric_val in self.model.summary_metrics.items():
                        target_name = f'val_{metric_name}'
                        tf.summary.scalar(target_name, metric_val.result(), step=epoch_step)

class VAE_trainer(Trainer):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def epoch_setup(self, epoch_step):
        # update capacity term
        self.model.kl_divergence.capacity_update(epoch_step)

    def epoch_writes(self, epoch_step):
        if self.writer is not None:
            with self.writer.as_default():
                super().epoch_writes(epoch_step)
                # extract images
                Z_mu, Z_log_var, Z = self.model.encode(self.X_val, training=False)
                x_hat = self.model.decode(Z_mu, training=False)
                # images of mean x vs x_hat vs diff
                x_img = np.mean(self.X_val, axis=0).reshape(len(self.labels),
                                                            len(self.distances))
                x_hat_img = np.mean(x_hat, axis=0).reshape(len(self.labels),
                                                           len(self.distances))
                # stack if passing data directly
                # stacked_img = np.vstack([x_img, x_hat_img])
                # stacked_img = np.reshape(stacked_img, (-1, len(labels), len(distances), 1))
                util_funcs.plt_setup()
                fig, axes = plt.subplots(1, 3, figsize=(6, 8))
                plot_funcs.plot_heatmap(axes[0], x_img, row_labels=self.labels,
                                        col_labels=self.distances)
                plot_funcs.plot_heatmap(axes[1], x_hat_img, set_row_labels=False,
                                        col_labels=self.distances)
                plot_funcs.plot_heatmap(axes[2], x_img - x_hat_img, set_row_labels=False,
                                        col_labels=self.distances)
                tf.summary.image('x | x hat | diff', plot_to_image(fig), step=epoch_step)
                # images of latents
                latent_dim = Z_mu.shape[1]
                util_funcs.plt_setup()
                fig, axes = plt.subplots(1, latent_dim, figsize=(12, 8))
                for l_idx in range(latent_dim):
                    corr = plot_funcs.correlate_heatmap(len(self.labels),
                                                        len(self.distances),
                                                        self.X_val,
                                                        Z_mu[:, l_idx])
                    plot_funcs.plot_heatmap(axes[l_idx],
                                            corr,
                                            row_labels=self.labels,
                                            set_row_labels=l_idx == 0,
                                            col_labels=self.distances)
                tf.summary.image('latents', plot_to_image(fig), step=epoch_step)
                # histograms
                if hasattr(self.model, 'sampling'):
                    tf.summary.histogram('Z mu biases',
                                         self.model.sampling.Z_mu_layer.weights[1],
                                         step=epoch_step)
                    tf.summary.histogram('Z mu weights',
                                         self.model.sampling.Z_mu_layer.weights[0],
                                         step=epoch_step)
                    tf.summary.histogram('Z logvar biases',
                                         self.model.sampling.Z_logvar_layer.weights[1],
                                         step=epoch_step)
                    tf.summary.histogram('Z logvar weights',
                                         self.model.sampling.Z_logvar_layer.weights[0],
                                         step=epoch_step)

class VaDE_trainer(VAE_trainer):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def epoch_setup(self, epoch_step):
        pass

    def batch_writes(self, batch_step):
        pass
        # super().batch_writes(batch_step)
        # # log scalars
        # if self.writer is not None:
        #     with self.writer.as_default():
        #         # image of cluster priors
        #         cat_pi_img = self.model.classify(self.X_val[:1000])
        #         cat_pi_img = np.reshape(cat_pi_img,
        #                                 (-1, cat_pi_img.shape[0], cat_pi_img.shape[1], 1))
        #         tf.summary.image('Batch cluster probabilities', cat_pi_img, step=batch_step)

    def epoch_writes(self, epoch_step):
        pass
        # super().epoch_writes(epoch_step)
        # # tensorboard
        # if self.writer is not None:
        #     with self.writer.as_default():
        #         # histograms
        #         tf.summary.histogram(f'GMM cat p', self.model.gamma.cat_pi, step=epoch_step)
        #         tf.summary.histogram(f'GMM mu', self.model.gamma.gmm_mu, step=epoch_step)
        #         tf.summary.histogram(f'GMM logvar', self.model.gamma.gmm_log_var,
        #                              step=epoch_step)

class GMVAE_trainer(Trainer):

    def __init__(self, lambda_threshold: float = 0.0, cv_weight: float = 1.0, **kwargs):
        super().__init__(**kwargs)
        self.lambda_threshold = lambda_threshold  # "Free bits threshold"
        self.cv_weight = cv_weight  # "Weight of the information theoretic cost term" **kwargs):

    @tf.function
    def training_step(self, batch_x, training):
        with tf.GradientTape() as tape:
            self.model(batch_x, training=training)
            recon_loss, exp_kld_loss, kld_loss, pz_h = self.model.losses
            loss = recon_loss + exp_kld_loss + kld_loss
            # max_h = -K.log(1.0 / self.k_components)
            if pz_h < self.lambda_threshold:
                pz_h = tf.stop_gradient(pz_h)
            loss += (pz_h - self.lambda_threshold) * self.cv_weight
        # Update the weights of the VAE.
        grads = tape.gradient(loss, self.model.trainable_weights)
        self.optimizer.apply_gradients(zip(grads, self.model.trainable_weights))
        if training:
            self.train_loss(loss)
        else:
            self.val_loss(loss)

    def epoch_writes(self, epoch_step):
        super().epoch_writes(epoch_step)
        if self.writer is not None:
            with self.writer.as_default():
                '''
                # images of latents
                QX_mu = self.model.encode(self.X_val, training=False)
                latent_dim = QX_mu.shape[1]
                phd_util.plt_setup()
                fig, axes = plt.subplots(1, latent_dim, figsize=(12, 8))
                for l_idx in range(latent_dim):
                    d = QX_mu[:, l_idx]
                    # catch NaN
                    # TODO: temporary workaround for NaN
                    if np.any(~np.isfinite(d)):
                        continue
                    corr = plot_funcs.correlate_heatmap(len(self.labels),
                                                        len(self.distances),
                                                        self.X_val,
                                                        d)
                    plot_funcs.plot_heatmap(axes[l_idx],
                                            corr,
                                            row_labels=self.labels,
                                            set_row_labels=l_idx == 0,
                                            col_labels=self.distances)
                '''
                # histograms
                tf.summary.histogram('QX mean',
                                     self.model.qx.qx_mu.weights[1],
                                     step=epoch_step)
                tf.summary.histogram('QX log var',
                                     self.model.qx.qx_log_var.weights[0],
                                     step=epoch_step)
                tf.summary.histogram('QW mean',
                                     self.model.qw.qw_mu.weights[1],
                                     step=epoch_step)
                tf.summary.histogram('QW log var',
                                     self.model.qw.qw_log_var.weights[0],
                                     step=epoch_step)


import numpy as np
import pandas as pd

#   Strategy simulator for the Twan dice game
# - two strategies are applied to players 1 and 2, a conservative and gambling profiles
# - code executes n random games with these two players and keeps the final score

# - players take turns starting with six dice, 1s and 5s are 100 and 50 points respectively. The rest are
#   reused in another round until a throw fails to bring points, that is a bust. A player can decide to
#   stop and keep the accumulated points and pass the play to the next player who can decide to 'gamble'
#   and continue playing with the round's points and dice or ask for a new set of six dice and 0 points.
#   If a six dice throw returns all different scores 1-6 that scores 1500 points and the player continues
#   with a fresh set of six. Also, if a player 'spends' all dice by scoring with 1s and/or 5s then again
#   a new set of six dice is awarded.

# - *spoiler alert*         <<<the conservative player wins more often>>>

def make_decision(results):
    dice_count = []
    for i in range(1, 7):
        dice_count.append((results == i).sum())

    if dice_count == [1, 1, 1, 1, 1, 1]:
        return 0, 1500

    dice_count = pd.Series(dice_count)

    if (dice_count[0] == 0) & (dice_count[4] == 0) & ((dice_count >= 3).sum() == 0):
        return 0, 0

    keep = []
    score = 0

    doubling = pd.Series(dice_count >= 3)

    if (doubling).sum() > 0:
        for j in dice_count.index.values:
            if dice_count.loc[j] >= 3:
                keep = np.concatenate((keep, (j+1)*np.ones(dice_count[j])))
                score = score + (j+1)*100*(2**(dice_count.loc[j]-3))
                dice_count[j] = 0

        if dice_count[0] + dice_count[4] == dice_count.sum():
            score = score + 100*dice_count[0] + 50*dice_count[4]
            dice_count = np.zeros(6)
    else:

        if dice_count[0] + dice_count[4] == dice_count.sum():
            score = score + 100*dice_count[0] + 50*dice_count[4]
            dice_count = np.zeros(6)

        if dice_count[0] > 0:
            score = score + 100
            dice_count[0] = dice_count[0]-1
        elif dice_count[4] > 0:
            score = score + 50
            dice_count[4] = dice_count[4]-1

    if score == 0:
        dice_count = np.zeros(6)

    if dice_count.sum() == 2:
        if dice_count[0] > 0:
            score = score + 100
            dice_count[0] = dice_count[0]-1
        elif dice_count[4] > 0:
            score = score + 50
            dice_count[4] = dice_count[4]-1

    return dice_count.sum(), score

def player_run(p_n_dice, p_score):
    print('Conservative player turn', p_n_dice, p_score)
    new_score = p_score

    while ((p_n_dice > 2) & (new_score > 0)) | ((p_n_dice == 6) & (new_score == 0)) | ((p_n_dice < 3) & (new_score > 1600)):
        p_results = np.random.randint(1, 7, size=p_n_dice)
        print(p_results)
        returned_dice, new_score = make_decision(p_results)

        if new_score == 0:
            p_score = 0
            break
        p_score = p_score + new_score
        p_n_dice = returned_dice
        if p_n_dice == 0:
            p_n_dice = 6
        print(returned_dice, new_score)
    return p_n_dice, p_score

def gambler_run(p_n_dice, p_score):

    print('Twan turn', p_n_dice, p_score)
    new_score = p_score

    while True:

        p_results = np.random.randint(1, 7, size=p_n_dice)
        print('Dice toss:', p_results)
        returned_dice, new_score = make_decision(p_results)
        print('Leftover dice:', returned_dice, '  New Score:', new_score)

        if (new_score == 0):
            p_score = 0
            break

        p_score = p_score + new_score
        p_n_dice = returned_dice
        if p_n_dice == 0:
            p_n_dice = 6

        if (p_n_dice < 3) & (p_score > 800):
            break

        if (p_n_dice == 1) & (p_score > 450):
            break

    print('Returned dice:', p_n_dice, 'Final score:', p_score)
    return p_n_dice, p_score

def play_dice():

    p1_score = 0
    p2_score = 0

    dice_in_play = 6
    returned_dice = 6
    score = 0
    while (p1_score < 10000) & (p2_score < 10000):

        if (returned_dice < 3) & (score > 1000):
            print('Start score:', p2_score)
            dice_in_play = returned_dice
            returned_dice, score = gambler_run(dice_in_play, score)
            p2_score = p2_score + score
            print('End score:', p2_score)

        else:
            dice_in_play = 6
            print('Start score:', p2_score)
            returned_dice, score = gambler_run(dice_in_play, 0)
            p2_score = p2_score + score
            print('End score:', p2_score)

        if (returned_dice < 3) & (score > 1000):
            print('Start score:', p1_score)
            dice_in_play = returned_dice
            returned_dice, score = player_run(dice_in_play, score)
            p1_score = p1_score + score
            print('End score:', p1_score)

        else:
            dice_in_play = 6
            print('Start score:', p1_score)
            returned_dice, score = player_run(dice_in_play, 0)
            p1_score = p1_score + score
            print('End score:', p1_score)

    if p1_score >= 10000:
        return 1
    else:
        return 0

if __name__ == '__main__':

    n_player1_wins = 0
    for i in range(100):
        n_player1_wins = n_player1_wins + play_dice()


N, M = map(int, input().split())
A = [list(map(int, input().split())) for _ in range(N)]

# 초기 구름 위치 설정
clouds = [(N - 1, 0), (N - 1, 1), (N - 2, 0), (N - 2, 1)]

# 좌표 이동 설정
dx = [0, -1, -1, -1, 0, 1, 1, 1]
dy = [-1, -1, 0, 1, 1, 1, 0, -1]
for _ in range(M):  # M번 반복
    d, s = map(int, input().split())

    # 구름 이동
    move_clouds = []
    for cloud_idx in range(len(clouds)):
        cloud_x, cloud_y = clouds[cloud_idx]
        # N을 더해 음의 좌표 처리, N나머지 연산으로 N보다 큰 좌표 처리
        next_cloud_x = (N + cloud_x + (dx[d - 1] * s)) % N
        next_cloud_y = (N + cloud_y + (dy[d - 1] * s)) % N

        move_clouds.append((next_cloud_x, next_cloud_y))  # 구름이 이동한 좌표를 리스트에 추가

    # 구름의 위치를 확인해주기 위한 N*N 행렬
    is_cloud = [[False] * N for _ in range(N)]
    # 구름의 위치에 따라 물 추가, 구름 위치 표기
    for mv_x, mv_y in move_clouds:
        A[mv_x][mv_y] += 1
        is_cloud[mv_x][mv_y] = True

    # 대각선 위치를 탐색해주기 위한 좌표 이동 인덱스 설정
    find_directions = [1, 3, 5, 7]
    # 구름의 대각선 위치를 검사하여 물 복사 버그
    for mv_x, mv_y in move_clouds:
        is_rain_count = 0
        # 대각선 칸에 물이 있는 칸이 몇 개 인지 세기
        for fd in find_directions:
            find_x, find_y = mv_x + dx[fd], mv_y + dy[fd]

            # 범위 밖인 경우 제외
            if not 0 <= find_x < N or not 0 <= find_y < N:
                continue
            # 물이 없는 경우 제외
            if A[find_x][find_y] == 0:
                continue

            is_rain_count += 1

        # 물이 있는 만큼 물 추가
        A[mv_x][mv_y] += is_rain_count

    # 새 구름 생성
    clouds = []
    for i in range(N):
        for j in range(N):
            # 물 양이 1이하이면 제외
            if A[i][j] <= 1:
                continue

            # 위에서 이동한 구름 위치가 아닌 경우, 구름 생성 및 물 양-2
            if not is_cloud[i][j]:
                clouds.append((i, j))
                A[i][j] -= 2

# 2차원 행렬 > 1차원 행렬 > sum 전체 합 계산, 출력
print(sum(sum(A, [])))

백준/Gold/21610. 마법사 상어와 비바라기/마법사 상어와 비바라기.py

마법사 상어와 비바라기.py


import os
import time
import logging
import json
import requests
import tempfile
import shutil
from collections import defaultdict
from repoze.lru import lru_cache

SLAVES_JSON_URL = "http://slavealloc.pvt.build.mozilla.org/api/slaves"
CACHE_FILE = "slaves.json"
CACHE_TTL = 10 * 60

log = logging.getLogger(__name__)

@lru_cache(10)
def get_classified_slaves(is_spot=True):
    js = get_slaves_json(SLAVES_JSON_URL, CACHE_FILE)
    slaves = [s for s in js if is_spot_slave(s) is is_spot and is_enabled(s)]
    # 2D dict: x[moz_type][region] = ["slave1", "slave2"]
    classified_slaves = defaultdict(lambda: defaultdict(set))
    for s in slaves:
        moz_type = slave_moz_type(s)
        region = slave_region(s)
        name = s.get("name")
        if all([moz_type, region, name]):
            classified_slaves[moz_type][region].add(name)
    return classified_slaves

def slave_region(slave):
    return slave.get("datacenter")

def is_spot_slave(slave):
    return "-spot-" in slave.get("name", "")

def is_enabled(slave):
    return slave.get("enabled")

def slave_moz_type(slave):
    # Separate golden slaves
    if slave.get("name") and "golden" in slave.get("name"):
        return "golden"

    # av-linux64
    if slave.get("name", "").startswith("av-linux64-") and \
       slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "centos6-mock" and \
       slave.get("purpose") == "build" and \
       slave.get("trustlevel") == "core":
        return "av-linux64"

    # bld-linux64
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "centos6-mock" and \
       slave.get("purpose") == "build" and \
       slave.get("trustlevel") == "core":
        return "bld-linux64"

    # try-linux64
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "centos6-mock" and \
       slave.get("purpose") == "build" and \
       slave.get("trustlevel") == "try":
        return "try-linux64"

    # tst-linux32
    if slave.get("bitlength") == "32" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "ubuntu32" and \
       slave.get("purpose") == "tests" and \
       slave.get("trustlevel") == "try":
        return "tst-linux32"

    # tst-linux64
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "ubuntu64" and \
       slave.get("purpose") == "tests" and \
       slave.get("speed") == "m1.medium" and \
       slave.get("trustlevel") == "try":
        return "tst-linux64"

    # tst-emulator64
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "ubuntu64" and \
       slave.get("purpose") == "tests" and \
       slave.get("speed") == "c3.xlarge" and \
       slave.get("trustlevel") == "try":
        return "tst-emulator64"

    # b-2008
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "win2k8" and \
       slave.get("purpose") == "build" and \
       slave.get("trustlevel") == "core":
        return "b-2008"

    # y-2008 (try-2008)
    if slave.get("bitlength") == "64" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "win2k8" and \
       slave.get("purpose") == "build" and \
       slave.get("trustlevel") == "try":
        return "y-2008"

    # t-w732
    if slave.get("bitlength") == "32" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "win7" and \
       slave.get("purpose") == "tests" and \
       slave.get("speed") == "c3.2xlarge" and \
       slave.get("trustlevel") == "try":
        return "t-w732"

    # g-w732
    if slave.get("bitlength") == "32" and \
       slave.get("environment") == "prod" and \
       slave.get("distro") == "win7" and \
       slave.get("purpose") == "tests" and \
       slave.get("speed") == "g2.2xlarge" and \
       slave.get("trustlevel") == "try":
        return "g-w732"

    return None

def get_slaves_json(url, cache):
    try:
        mtime = os.stat(cache).st_mtime
        now = time.time()
        if now - mtime < CACHE_TTL:
            log.debug("Using cached slaves.json")
            return read_slaves_json(cache)
        else:
            log.debug("File expired, fetching a new one")
    except (OSError, IOError, KeyError):
        log.warn("Error reading cache file, trying to fetch", exc_info=True)

    try:
        download_file(url, cache)
    except:  # noqa: E722
        log.warn("Cannot fetch slaves.json, reusing the existing file",
                 exc_info=True)
    return read_slaves_json(cache)

def read_slaves_json(filename):
    return json.load(open(filename))

def download_file(url, dest):
    req = requests.get(url, timeout=30)
    req.raise_for_status()
    _, tmp_fname = tempfile.mkstemp()
    with open(tmp_fname, "wb") as f:
        f.write(req.content)
    log.debug("moving %s to %s", tmp_fname, dest)
    shutil.move(tmp_fname, dest)


from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from .models import Order, Option, Product, Price, Cart, Cart_Item
# from django.contrib.auth.models import User
from users.models import User
@receiver(post_save, sender=Order)
def create_options(sender, instance, created, **kwargs):
    if created:
        for t in range(instance.selection_limit + 1):
            Option.objects.get_or_create(order=instance, selection_count=t)

@receiver(post_save, sender=Option)
def create_prices(sender, instance, created, **kwargs):
    if created:
        if instance.order.size_choice:
            Price.objects.get_or_create(option=instance, size="Large")
            Price.objects.get_or_create(option=instance, size="Small")
        else:
            Price.objects.get_or_create(option=instance)

@receiver(post_save, sender=User)
def create_cart(sender, instance, created, **kwargs):
    if created:
        Cart.objects.get_or_create(user=instance)

@receiver(pre_delete, sender=Cart)
def remove_cart_item(sender, instance, using, **kwargs):
    for cart_item in instance.cart_items.all():
        Cart_Item.objects.filter(id=cart_item.id).delete()


'''
给你一个数组 nums，我们可以将它按一个非负整数 k 进行轮调，这样可以使数组变为 [nums[k], nums[k + 1], ... nums[nums.length - 1], nums[0], nums[1], ..., nums[k-1]] 的形式。此后，任何值小于或等于其索引的项都可以记作一分。

例如，数组为 nums = [2,4,1,3,0]，我们按 k = 2 进行轮调后，它将变成 [1,3,0,2,4]。这将记为 3 分，因为 1 > 0 [不计分]、3 > 1 [不计分]、0 <= 2 [计 1 分]、2 <= 3 [计 1 分]，4 <= 4 [计 1 分]。
在所有可能的轮调中，返回我们所能得到的最高分数对应的轮调下标 k 。如果有多个答案，返回满足条件的最小的下标 k 。

示例 1：

输入：nums = [2,3,1,4,0]
输出：3
解释：
下面列出了每个 k 的得分：
k = 0,  nums = [2,3,1,4,0],    score 2
k = 1,  nums = [3,1,4,0,2],    score 3
k = 2,  nums = [1,4,0,2,3],    score 3
k = 3,  nums = [4,0,2,3,1],    score 4
k = 4,  nums = [0,2,3,1,4],    score 3
所以我们应当选择 k = 3，得分最高。
示例 2：

输入：nums = [1,3,0,2,4]
输出：0
解释：
nums 无论怎么变化总是有 3 分。
所以我们将选择最小的 k，即 0。
'''
import enum

class Solution(object):
    def bestRotation(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        n = len(nums)
        diffs = [0] * n
        for i, num in enumerate(nums):
            low = (i + 1) % n
            high = (i - num + n + 1) % n
            diffs[low] += 1
            diffs[high] -= 1
            if low >= high:
                diffs[0] += 1
        score, maxScore, idx = 0, 0, 0
        for i, diff in enumerate(diffs):
            score += diff
            if score > maxScore:
                maxScore, idx = score, i
        return idx


# -*- coding: utf-8 -*-
# referred: https://blog.csdn.net/Crafts_Neo/article/details/90356621

import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler

path = "./data/"

train = pd.read_csv(path + 'train.csv',engine = 'python', encoding = 'utf-8')
test  = pd.read_csv(path + 'test.csv' ,engine = 'python', encoding = 'gbk')

train = train[train['observation'] == 'PM2.5']
# print(train)
test = test[test['AMB_TEMP'] == 'PM2.5']
# print(test)
train = train.drop(['Date','stations','observation'],axis=1)
# print(train)
test_x = test.iloc[:,2:]
#print(test_x)

train_x = []
train_y = []

for i in range(15):
        x = train.iloc[:,i:i+9]
        x.columns = np.array(range(9))
        y=train.iloc[:,i+9]
        y.columns = np.array(range(1))
        train_x.append(x)
        train_y.append(y)

train_x = pd.concat(train_x)
train_y = pd.concat(train_y)

train_y = np.array(train_y,float)
test_x  = np.array(test_x,float)
#print(test_x)
#print(train_y)
ss = StandardScaler()

#print(ss)
print("-----")
ss.fit(train_x)
#print(ss)
train_x = ss.transform(train_x)

ss.fit(test_x) # what's the meaning?
test_x = ss.transform(test_x)

def r2_score(y_true,y_predict):
    MSE = np.sum((y_true-y_predict)**2)/len(y_true)
    return 1-MSE/np.var(y_true)

class LinearRegression:
    def __init__(self):
        self.coef_ = None
        self.intercept_ = None
        self._theta = None

    def fit_normal(self,X_train,y_train):
        assert X_train.shape[0] == y_train.shape[0], \
                "the size of X_train must be equal to the size of y_train"

        X_b = np.hstack([np.ones((len(X_train),1)),X_train])
        self._theta = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y_train)

        self.intercept_ = self._theta[0]
        self.coef_ = self._theta[1:]

        return self

    def fit_gd(self,X_train,y_train,eta = 0.01, n_iters = 1e4):
        assert X_train.shape[0] == y_train.shape[0], \
                "the size of X_train must be equal to the size of y_train"

        def J(theta,X_b,y):
            try:
                return np.sum((y-X_b.dot(theta))**2)/len(y)
            except:
                return float("inf")

        def dJ(theta,X_b,y):
            return X_b.T.dot(X_b.dot(theta)-y)*2./len(y)

        def gradient_descent(X_b,y,initial_theta,eta,n_iters = 1e4,epsilon=1e-8):
            theta = initial_theta
            cur_iter = 0
            while cur_iter < n_iters:
                gradient = dJ(theta,X_b,y)
                last_theta = theta
                theta = theta-eta*gradient
                if (abs(J(theta,X_b,y)-J(last_theta,X_b,y)) < epsilon):
                    break
                cur_iter += 1
            return theta

        X_b = np.hstack([np.ones((len(X_train),1)),X_train])
        initial_theta = np.zeros(X_b.shape[1])
        self._theta = gradient_descent(X_b,y_train,initial_theta,eta,n_iters)
        self.intercept_ = self._theta[0]
        self.coef_ = self._theta[1:]
        return self

    def predict(self,X_predict):
        assert self.intercept_ is not None and self.coef_ is not None,\
                "must fit before predict!"
        assert X_predict.shape[1] == len(self.coef_), \
                "the feature number of X_predict must be equal to X_train"
        X_b = np.hstack([np.ones((len(X_predict),1)),X_predict])
        return X_b.dot(self._theta)

    def score(self,X_test,y_test):
        y_predict = self.predict(X_test)
        return r2_score(y_test,y_predict)

    def __repr__(self):
        return

LR = LinearRegression().fit_gd(train_x,train_y)
LR.score(train_x,train_y)
result = LR.predict(test_x)

sampleSubmission = pd.read_csv(path+'sampleSubmission.csv',engine='python',encoding='gbk')
sampleSubmission['value'] = result
sampleSubmission.to_csv(path+'result.csv')


from django.urls import path
from .views import *

urlpatterns=[
    path("add-cart-item/<int:cartitem_id>/", add_cart_item, name="add-cart-item"),
    path("subtract-cart-item/<int:cartitem_id>/",subtract_cart_item, name="subtract-cart-item"),
    path("remove-cart-item/<int:cartitem_id>/",remove_cart_item, name="remove-cart-item"),
    path("cart/", cart, name="cart"),
    path("cart/add-to-cart/", add_to_cart, name="add_to_cart"),
    path("wishlist/<int:pk>/<str:data>/", add_to_wishlist, name="add-to-wishlist"),
    path("wishlist/", wishlist_items, name="wishlist"),
    path("remove-wishlist/<int:pk>/<str:data>/", remove_wishlist, name="remove-wishlist"),
]


import json
import logging

import aiohttp
from discord.ext import commands

logger = logging.getLogger(__name__)

class InspireQuote(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        # bot.slash_command(guild_ids=guild_ids)(self.inspire)

    @commands.slash_command()
    async def inspire(self, ctx):
        await ctx.respond(await self.get_quote())

    async def get_quote(self):
        async with aiohttp.ClientSession() as session:
            async with session.get("https://zenquotes.io/api/random") as r:
                if r.status == 200:
                    json_data = json.loads(await r.text())
                    quote = json_data[0]["q"] + " -" + json_data[0]["a"]
                    return quote


import setuptools

with open("README.md", "r", encoding="utf-8") as fh:
    long_description = fh.read()

setuptools.setup(
    name="fermentools",
    version_config=True,
    setup_requires=["setuptools-git-versioning"],
    author="Pau Cabaneros",
    author_email="pau.cabaneros@gmail.com",
    description="Package to teach chemometrics in the context of fermentation processes",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/paucablop/fermentools",
    project_urls={
        "Bug Tracker": "https://github.com/paucablop/fermentools/issues/",
    },
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    package_dir={"": "."},
    packages=setuptools.find_packages(where="."),
    python_requires=">=3.10",
    install_requires=[
        "numpy",
        "pandas",
        "matplotlib",
        "mbpls",
        "scipy",
        "scikit-learn",
    ],
    include_package_data=True,
    package_data={'': ['datasets/data/*.csv']}
)


import sys
import os

import arcade
import arcade.gui as gui
import pyglet

import badwing.app
from badwing.constants import *
from badwing.assets import asset
from badwing.level import Level
from badwing.controller import Controller

from badwing.physics.dynamic import DynamicPhysicsEngine
from badwing.physics.kinematic import KinematicPhysicsEngine
from badwing.layer import Layer
from badwing.barrier import BarrierLayer
from badwing.background import BackgroundLayer
from badwing.tile import TileLayer

from badwing.characters.butterfly import Butterflies

from badwing.firework import Firework
from badwing.debug import DebugLayer

from badwing.scenes.level1 import Level1

class EndScreen(Level):
    def __init__(self):
        super().__init__('start')
        # Our physics engine
        #self.physics_engine = physics_engine = KinematicPhysicsEngine()
        self.physics_engine = physics_engine = DynamicPhysicsEngine()
        self.space = physics_engine.space

        self.width = badwing.app.game.width
        self.height = badwing.app.game.height
        self.half_width = self.width/2
        self.half_height = self.height/2
        self.center_x = self.width / 2
        self.center_y = self.height / 2

        # Used to keep track of our scrolling
        self.view_bottom = 0
        self.view_left = 0

        # Keep track of the score
        self.score = 0

        # Load sounds
        self.collect_coin_sound = arcade.load_sound(":resources:sounds/coin1.wav")
        self.jump_sound = arcade.load_sound(":resources:sounds/jump1.wav")

    @classmethod
    def create(self):
        level = EndScreen()
        level.setup()
        return level

    #next level
    def get_next_level(self):
        import badwing.scenes.level1
        return badwing.scenes.level1.Level1

    def add_buttons(self):
        width = 200
        height = 50

        quit_button = gui.UIFlatButton(0 , 0, width, height, "Quit")
        @quit_button.event("on_click")
        def submit(x):
          pyglet.app.exit()

        self.ui_manager.add(
            gui.UIAnchorLayout(
                children=[gui.UIBoxLayout(
                    children=[quit_button]
                )]
            )
        )

    def do_setup(self):
        self.theme = badwing.app.game.theme
        super().do_setup()

        # Used to keep track of our scrolling
        self.view_bottom = 0
        self.view_left = 0

        # Keep track of the score
        self.score = 0

        self.add_layer(BarrierLayer(self, 'barrier'))
        self.add_layer(BackgroundLayer(self, 'background', ":resources:images/backgrounds/abstract_1.jpg"))
        self.ladder_layer = self.add_layer(TileLayer(self, 'ladder'))
        self.spark_layer = self.add_layer(Layer(self, 'spark'))

        self.add_layer(BackgroundLayer(self, 'background', ":resources:images/backgrounds/abstract_1.jpg"))
        self.butterfly_layer = butterfly_layer = Layer(self, 'butterflies')
        butterflies = Butterflies.create_random(20, (0,0,SCREEN_WIDTH,SCREEN_HEIGHT))
        self.butterfly_layer.add_model(butterflies)
        self.add_animated_layer(butterfly_layer)

        self.add_buttons()

    def post_setup(self):
        super().post_setup()
        self.push_controller(Controller())

    def update(self, delta_time):
        super().update(delta_time)
        self.physics_engine.update()

    def draw(self):
        super().draw()
        arcade.draw_text(
            "You Win!!!", self.center_x-100, self.center_y + 100, arcade.color.WHITE, 60, font_name='Verdana', align='center', width=500
        )

    def on_key_press(self, key, modifiers):
        if key == arcade.key.ESCAPE:
            pyglet.app.exit()


import subprocess
import time
import json
import webbrowser
import pyscreenshot as ImageGrab
from difflib import get_close_matches
import random
from random import choice
from pynput.keyboard import Key, Controller

def isContain(text, lst):
	for word in lst:
		if word in text:
			return True
	return False

# FUNCTION: Window App Operations (Maximize, Minimize, Close, Move, Switch) =======================================
class WindowOpt:
	def __init__(self):
		self.keyboard = Controller()

	def openWindow(self):
		self.maximizeWindow()

	def closeWindow(self):
		self.keyboard.press(Key.alt_l)
		self.keyboard.press(Key.f4)
		self.keyboard.release(Key.f4)
		self.keyboard.release(Key.alt_l)

	def minimizeWindow(self):
		for i in range(2):
			self.keyboard.press(Key.cmd)
			self.keyboard.press(Key.down)
			self.keyboard.release(Key.down)
			self.keyboard.release(Key.cmd)
			time.sleep(0.05)

	def maximizeWindow(self):
		self.keyboard.press(Key.cmd)
		self.keyboard.press(Key.up)
		self.keyboard.release(Key.up)
		self.keyboard.release(Key.cmd)

	def moveWindow(self, query):
		self.keyboard.press(Key.cmd)

		if "left" in query:
			self.keyboard.press(Key.left)
			self.keyboard.release(Key.left)
		elif "right" in query:
			self.keyboard.press(Key.right)
			self.keyboard.release(Key.right)
		elif "down" in query:
			self.keyboard.press(Key.down)
			self.keyboard.release(Key.down)
		elif "up" in query:
			self.keyboard.press(Key.up)
			self.keyboard.release(Key.up)
		self.keyboard.release(Key.cmd)

	def switchWindow(self):
		self.keyboard.press(Key.alt_l)
		self.keyboard.press(Key.tab)
		self.keyboard.release(Key.tab)
		self.keyboard.release(Key.alt_l)

	def takeScreenShot(self):
		name = 'screenshots/AI-ss_' + str(random.randint(0, 1000000)) + '.png'
		image = ImageGrab.grab()
		image.show()
		image.save(name)

def winOpt(query):
	w = WindowOpt()
	if isContain(query, ['open']):
		w.openWindow()
	elif isContain(query, ['close']):
		w.closeWindow()
	elif isContain(query, ['mini']):
		w.minimizeWindow()
	elif isContain(query, ['maxi']):
		w.maximizeWindow()
	elif isContain(query, ['move', 'slide']):
		w.moveWindow(query)
	elif isContain(query, ['switch']):
		w.switchWindow()
	elif isContain(query, ['screenshot']):
		w.takeScreenShot()
	return

# FUNCTION: Tab Operations (New Tab, Close Tab, Switch Tab) =================================================
class TabOpt:
	def __init__(self):
		self.keyboard = Controller()

	def newTab(self):
		self.keyboard.press(Key.ctrl)
		self.keyboard.press('n')
		self.keyboard.release('n')
		self.keyboard.release(Key.ctrl)

	def closeTab(self):
		self.keyboard.press(Key.ctrl)
		self.keyboard.press('w')
		self.keyboard.release('w')
		self.keyboard.release(Key.ctrl)

	def switchTab(self):
		self.keyboard.press(Key.ctrl)
		self.keyboard.press(Key.tab)
		self.keyboard.release(Key.tab)
		self.keyboard.release(Key.ctrl)

	def switchPrevTab(self):
		self.keyboard.press(Key.ctrl)
		self.keyboard.press(Key.shift)
		self.keyboard.press(Key.tab)
		self.keyboard.release(Key.tab)
		self.keyboard.release(Key.shift)
		self.keyboard.release(Key.ctrl)

	def reopenTab(self):
		self.keyboard.press(Key.ctrl)
		self.keyboard.press(Key.shift)
		self.keyboard.press('t')
		self.keyboard.release('t')
		self.keyboard.release(Key.shift)
		self.keyboard.release(Key.ctrl)

def tabOpt(query):
	t = TabOpt()
	if isContain(query, ['new', 'open', 'another', 'create']):
		t.newTab()
	elif isContain(query, ['close', 'delete']):
		t.closeTab()
	elif isContain(query, ['switch', 'move', 'another', 'next', 'which']):
		t.switchTab()
	elif isContain(query, ['previous']):
		t.switchPrevTab()
	elif isContain(query, ['previous', 'tab']) and 'reopen' in query:
		t.switchPrevTab()
	else:
		return

# FUNCTION: Write and Save ==============================================================
class SystemTasks:
	def __init__(self):
		self.keyboard = Controller()

	def write(self, text):
		text = text[5:] + " "
		for char in text:
			self.keyboard.type(char)
			time.sleep(0.02)

	def select(self):
		self.keyboard.press(Key.ctrl)
		self.keyboard.press('a')
		self.keyboard.release('a')
		self.keyboard.release(Key.ctrl)

	def hitEnter(self):
		self.keyboard.press(Key.enter)
		self.keyboard.release(Key.enter)

	def delete(self):
		self.keyboard.press(Key.backspace)
		self.keyboard.release(Key.enter)

	def save(self, text):
		if "don't" in text:
			self.keyboard.press(Key.right)
		else:
			self.keyboard.press(Key.ctrl)
			self.keyboard.press('s')
			self.keyboard.release('s')
			self.keyboard.release(Key.ctrl)
		self.hitEnter()

def systemOpt(query):
	s = SystemTasks()
	if 'delete' in query:
		s.delete()
	elif 'save' in query:
		s.save(query)
	elif 'type' in query:
		s.write(query)
	elif 'select' in query:
		s.select()
	elif 'enter' in query:
		s.hitEnter()

# FUNCTION: Access System application & Websites ==============================================================
class AccessApp:
	def __init__(self):
		self.keyboard = Controller()

	def openApp(self, appName):
		program = ['notepad', 'mspaint', 'write', 'calc', 'control', 'cmd', 'SnippingTool']
		appName = appName.replace('paint', 'mspaint')
		appName = appName.replace('wordpad', 'write')
		appName = appName.replace('word', 'write')
		appName = appName.replace('calculator', 'calc')
		appName = appName.replace('control panel', 'control')
		appName = appName.replace('command prompt', 'cmd')
		appName = appName.replace('snipping tool', 'SnippingTool')
		try:
			print(appName[5:])
			if appName[5:] not in program:
				return False
			print("Entered")
			subprocess.Popen('C:\\Windows\\System32\\' + appName[5:] + '.exe')
		except:
			pass

	def open_website(self, query):
		data = json.load(open('otherfiles/websites.json', encoding='utf-8'))
		query = query.replace('open', '')
		if query in data:
			response = data[query]
		else:
			query = get_close_matches(query, data.keys(), n=2, cutoff=0.5)
			if len(query) == 0: return False
			response = choice(data[query[0]])

		webbrowser.open(response)

def accessApp(query):
	s = AccessApp()
	if isContain(query, ['notepad', 'paint', 'calc', 'word', 'control panel', 'command prompt', 'snipping tool']):
		return s.openApp(query)
	else:
		return s.open_website(query)


#!/usr/bin/python
# see https://pypi.org/project/rpm-make-rules-dependency-lister/

"""A setuptools based setup module.
See:
https://packaging.python.org/en/latest/distributing.html
https://github.com/pypa/sampleproject
"""

# Always prefer setuptools over distutils
from setuptools import setup
# To use a consistent encoding
from codecs import open
from os import path

here = path.abspath(path.dirname(__file__))

# Get the long description from the README file
with open(path.join(here, 'README.md'), encoding='utf-8') as f:
    long_description = f.read()

setup(
    name='rpm-make-rules-dependency-lister',
    packages=['rpm_make_rules_dependency_lister'],
    python_requires='>=3',
    version='1.11.1',
    description='Module for analyzing RPM dependencies and speedup RPM building process.',
    long_description=long_description,  # Optional
    long_description_content_type='text/markdown',  # Optional (see note above)
    author='Francesco Montorsi',
    author_email='francesco.montorsi@gmail.com',
    url='https://github.com/f18m/rpm-make-rules-dependency-lister',
    download_url='https://github.com/f18m/rpm-make-rules-dependency-lister/archive/v1.11.1.tar.gz',
    keywords=['RPM', 'GNU make', 'dependency'],
    classifiers=[
        'Programming Language :: Python :: 3',
        'Development Status :: 5 - Production/Stable',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: MIT License',
        'Operating System :: POSIX'
    ],
    entry_points={
        'console_scripts': [
            'rpm_make_rules_dependency_lister = rpm_make_rules_dependency_lister.rpm_frontend:main',
        ]
    }
)


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

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from __future__ import print_function

import argparse
import json
import logging
import sys

try:
    import pygments
    import pygments.formatters
    import pygments.lexers
    import pygments.util
except ImportError:
    pygments = None

from keystoneauth1 import exceptions
from keystoneauth1 import session
import os_client_config

from os_http import version

LOG = logging.getLogger(__name__)

_occ_version = os_client_config.__version__

formatter_name = 'console' if sys.stdout.isatty() else 'text'

class ErrorExit(Exception):

    def __init__(self, message, exit_code=1):
        self.message = message
        self.exit_code = exit_code

def format_resp(resp):
    # I can see no way to get the HTTP version
    headers = ["HTTP/1.1 %d %s" % (resp.status_code, resp.reason or '')]
    headers.extend('%s: %s' % k for k in resp.headers.items())
    headers = '\n'.join(headers)

    if 'json' in resp.headers.get('Content-Type', '').lower():
        body = json.dumps(resp.json(), sort_keys=True, indent=4)
    else:
        body = resp.content

    if pygments:
        mime = resp.headers.get('Content-Type')
        http_lexer = pygments.lexers.get_lexer_by_name('http')
        formatter = pygments.formatters.get_formatter_by_name(formatter_name)

        try:
            body_lexer = pygments.lexers.get_lexer_for_mimetype(mime)
        except pygments.util.ClassNotFound:
            body_lexer = pygments.lexers.get_lexer_by_name('text')

        headers = pygments.highlight(headers, http_lexer, formatter)
        body = pygments.highlight(body, body_lexer, formatter)

    return '\n'.join([headers, '', body])

def run(argv):
    parser = argparse.ArgumentParser(
        description='Simple HTTP testing for Openstack')

    parser.add_argument('--debug',
                        action='store_true',
                        help='Enable debug output')

    config = os_client_config.OpenStackConfig()
    config.register_argparse_arguments(parser, argv)

    positional = parser.add_argument_group('Positional Arguments')
    positional.add_argument('method',
                            metavar='METHOD',
                            help='The HTTP method to make the request with')
    positional.add_argument('url',
                            metavar='URL',
                            help='The URL or path to request')
    positional.add_argument('items',
                            metavar='ITEM',
                            nargs='*',
                            help='Additional items')

    opts = parser.parse_args(argv)

    if opts.debug:
        logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.WARN)

    cloud = config.get_one_cloud(argparse=opts)

    adap = cloud.get_session_client(None)

    # FIXME(jamielennox): These things should be handled by os-client-config
    adap.logger = LOG
    adap.user_agent = 'os-http/%s os-client-config/%s %s' % (
        version.version_string,
        _occ_version,
        session.DEFAULT_USER_AGENT)
    adap.version = opts.os_api_version

    headers = {'Accept': 'application/json'}

    for item in opts.items:
        if ':' in item:
            key, val = item.split(':', 1)
            headers[key] = val
        else:
            raise ErrorExit("Unknown item: %s" % item)

    try:
        resp = adap.request(opts.url,
                            opts.method.upper(),
                            headers=headers,
                            raise_exc=False)

    except exceptions.EmptyCatalog:
        message = ("Failed to find an endpoint because the returned service "
                   "catalog is empty.")

        try:
            get_access = adap.session.auth.get_access
        except AttributeError:
            # not an identity plugin - see if it specifies project_id
            scoped = bool(adap.get_project_id())
        else:
            scoped = get_access(adap.session).scoped

        if not scoped:
            message += (" This seems to be because the credentials provided "
                        "result in an unscoped token. Please check your "
                        "authentication credentials.")

        raise ErrorExit(message)

    except exceptions.EndpointNotFound:
        service_params = ('service_type',
                          'service_name',
                          'interface',
                          'region_name',
                          'version')

        query = ", ".join("%s=%s" % (p, getattr(adap, p))
                          for p in service_params if getattr(adap, p))
        raise ErrorExit("Failed to find an endpoint in the service catalog "
                        "that matches your query: %s" % query)

    return format_resp(resp)

def main(argv=sys.argv[1:]):
    try:
        output = run(argv)
    except ErrorExit as e:
        LOG.error(e.message)
        sys.exit(e.exit_code)
    else:
        print(output)


#!/usr/bin/python

import os
import errno

#dirf = "/afs/inf.ed.ac.uk/group/ug4-projects/s1413557/output-delme/"
#dirf = "/afs/inf.ed.ac.uk/group/ug4-projects/s1413557/CNNoutput/"
dirf = "/afs/inf.ed.ac.uk/group/project/F4KC/final/"

for i in range(256):
	char = hex(i)[2:]
	if len(char) == 1:
		char = "0" + char
	filename = dirf + char[0] + "/" + char + "/"
	os.makedirs(os.path.dirname(filename))
	print(filename)


###Using a Semaphore to synchronize threads

import threading
import time
import random

##The optional argument gives the initial value for the internal counter;
##it defaults to 1.
##If the value given is less than 0, ValueError is raised.
semaphore = threading.Semaphore(0)

def consumer():
    print ("consumer is waiting.")
    ##Acquire a semaphore
    semaphore.acquire()
    ##The consumer have access to the shared resource
    print ("Consumer notify : consumed item number %s " %item)

def producer():
    global item
    time.sleep(10)
    ##create a random item
    item = random.randint(0,1000)
    print ("producer notify : producted item number %s" %item)

    ##Release a semaphore, incrementing the internal counter by one.
    ##When it was zero on entry and another thread is waiting for it
    ##to become larger than zero again, wake up that thread.
    semaphore.release()

#Main program
if __name__ == '__main__':
    for i in range (0,5) :
        t1 = threading.Thread(target=producer)
        t2 = threading.Thread(target=consumer)
        t1.start()
        t2.start()
        t1.join()
        t2.join()
    print ("program terminated")


from itertools import groupby
from operator import itemgetter
from math import ceil

def solve(n, k, tickets):
    unpicked = [num for num in range(1, k+1) if num not in tickets]
    intervals = []

    for i, g in groupby(enumerate(unpicked), lambda ix : ix[0] - ix[1]):
        intervals.append(list(map(itemgetter(1), g)))

    highest = 0
    mem = 0

    for interval in intervals:
        if len(interval) == 1:
            score = 1
        elif 1 in interval or k in interval:
            score = len(interval)
        else:
            score = ceil(len(interval)/2)

        if score >= highest:
            mem = highest
            highest = score

    total_score = highest + mem

    for interval in intervals:
        if len(interval) > 1 and (1 not in interval or k not in interval):
            score = len(interval)
        if score > total_score:
            total_score = score

    result = total_score/k
    return result

if __name__ == '__main__':
    t = int(input())
    for case in range(1, t+1):
        n, k = list(map(int, input().split()))
        tickets = list(map(int, input().split()))
        result = solve(n, k, tickets)
        print('Case #{}: {}'.format(case, result))


from collections import defaultdict

from math import cos, sin, atan, pi
from operator import itemgetter
from typing import Optional, Tuple, Iterator

import numpy as np
from kivy.uix.widget import Widget
from kivy.core.text import Label
from kivy.graphics.fbo import Fbo
from kivy.graphics import (Translate, Rectangle, Quad, Color, InstructionGroup)
from kivy.properties import (NumericProperty, ListProperty, ObjectProperty)
from kivy.clock import Clock, mainthread

try:
	from kivy.garden.collider import Collide2DPoly
except (KeyError, ImportError):
	from ..collide import Collide2DPoly
from ..util import get_thin_rect_vertices, fortyfive, \
 DEFAULT_ARROW_LABEL_KWARGS

cos45 = cos(fortyfive)
sin45 = sin(fortyfive)

def up_and_down(orig, dest, taillen):
	if orig.center_y == dest.center_y:
		raise ValueError("Can't draw an arrow at a point")
	flipped = orig.center_y > dest.center_y
	if flipped:
		orig, dest = dest, orig
	x = int(orig.center_x)
	dy = int(dest.y)
	oy = int(orig.top)
	if flipped:
		oy, dy = dy, oy
	off1 = cos45 * taillen
	off2 = sin45 * taillen
	x0 = x
	y0 = oy
	endx = x
	endy = dy
	x1 = endx - off1
	x2 = endx + off1
	y1 = y2 = endy - off2 if oy < dy else endy + off2
	return [x0, y0, endx, endy], [x1, y1, endx, endy, x2, y2]

def left_and_right(orig, dest, taillen):
	if orig.center_x == dest.center_x:
		raise ValueError("Can't draw an arrow at a point")
	flipped = orig.center_x > dest.center_x
	if flipped:
		orig, dest = dest, orig
	y = int(orig.center_y)
	dx = int(dest.x)
	ox = int(orig.right)
	if flipped:
		ox, dx = dx, ox
	off1 = cos45 * taillen
	off2 = sin45 * taillen
	x0 = ox
	y0 = y
	endx = dx
	endy = y
	y1 = endy - off1
	y2 = endy + off1
	x1 = x2 = endx - off2 if ox < dx else endx + off2
	return [x0, y0, endx, endy], [x1, y1, endx, endy, x2, y2]

def _get_points_first_part(orig, dest, taillen):
	ox, oy = orig.center
	ow, oh = orig.size
	dx, dy = dest.center
	dw, dh = dest.size
	# Flip the endpoints around so that the arrow faces up and right.
	# We'll flip it back at the end as needed.
	xco = 1 if ox < dx else -1
	yco = 1 if oy < dy else -1
	ox *= xco
	dx *= xco
	oy *= yco
	dy *= yco
	# Nudge my endpoints so that I start and end at the edge of
	# a Spot.
	if dy - oy > dx - ox:
		topy = dy - dh / 2
		boty = oy + oh / 2
		leftx = ox
		rightx = dx
	else:
		leftx = ox + ow / 2
		rightx = dx - dw / 2
		topy = dy
		boty = oy
	# Degenerate cases.
	# Also, these need to be handled specially to avoid
	# division by zero later on.
	if rightx == leftx:
		return up_and_down(orig, dest, taillen)
	if topy == boty:
		return left_and_right(orig, dest, taillen)
	return ow, oh, dw, dh, xco, leftx, rightx, yco, topy, boty

def get_points_multi(args):
	"""Return a dictionary mapping (orig, dest) to pairs of point lists for arrows

	Takes an iterable of (orig, dest, taillen) where orig and dest are Spot instances

	taillen is an integer specifying how long the arrowhead should be.

	"""
	ret = {}
	keys = []
	topys = []
	botys = []
	leftxs = []
	rightxs = []
	taillens = []
	xcos = []
	ycos = []
	for (orig, dest, taillen) in args:
		try:
			p1 = _get_points_first_part(orig, dest, taillen)
		except ValueError:
			p1 = 2, 2, 2, 2, 1, 0, 1, 1, 0, 1
		if len(p1) == 2:
			ret[orig.name, dest.name] = p1
			continue
		ow, oh, dw, dh, xco, leftx, rightx, yco, topy, boty = p1
		keys.append((orig.name, dest.name))
		leftxs.append(leftx)
		rightxs.append(rightx)
		topys.append(topy)
		botys.append(boty)
		taillens.append(taillen)
		xcos.append(xco)
		ycos.append(yco)
	topys = np.array(topys)
	botys = np.array(botys)
	rightxs = np.array(rightxs)
	leftxs = np.array(leftxs)
	rises = np.subtract(topys, botys)
	runs = np.subtract(rightxs, leftxs)
	slopes = np.divide(rises, runs)
	unslopes = np.divide(runs, rises)
	start_thetas = np.arctan(slopes)
	end_thetas = np.arctan(unslopes)
	top_thetas = np.subtract(start_thetas, fortyfive)
	bot_thetas = np.subtract(pi - fortyfive, end_thetas)
	topsins = np.sin(top_thetas)
	topcoss = np.cos(top_thetas)
	botsins = np.sin(bot_thetas)
	botcoss = np.cos(bot_thetas)
	taillens = np.array(taillens)
	xoff1s = np.multiply(topcoss, taillens)
	yoff1s = np.multiply(topsins, taillens)
	xoff2s = np.multiply(botcoss, taillens)
	yoff2s = np.multiply(botsins, taillens)
	xcos = np.array(xcos)
	ycos = np.array(ycos)
	x1s = np.multiply(np.subtract(rightxs, xoff1s), xcos)
	x2s = np.multiply(np.subtract(rightxs, xoff2s), xcos)
	y1s = np.multiply(np.subtract(topys, yoff1s), ycos)
	y2s = np.multiply(np.subtract(topys, yoff2s), ycos)
	startxs = np.multiply(leftxs, xcos)
	startys = np.multiply(botys, ycos)
	endxs = np.multiply(rightxs, xcos)
	endys = np.multiply(topys, ycos)
	for key, startx, starty, endx, endy, x1, y1, endx, endy, x2, y2 in zip(
		keys, startxs, startys, endxs, endys, x1s, y1s, endxs, endys, x2s,
		y2s):
		ret[key] = ([startx, starty, endx, endy], [x1, y1, endx, endy, x2, y2])
	return ret

def get_points(orig, dest, taillen):
	"""Return a pair of lists of points for use making an arrow.

	The first list is the beginning and end point of the trunk of the arrow.

	The second list is the arrowhead.

	"""
	p1 = _get_points_first_part(orig, dest, taillen)
	if len(p1) == 2:
		return p1
	ow, oh, dw, dh, xco, leftx, rightx, yco, topy, boty = p1
	rise = topy - boty
	run = rightx - leftx
	slope = rise / run
	unslope = run / rise
	start_theta = atan(slope)
	end_theta = atan(unslope)

	# make the little wedge at the end so you can tell which way the
	# arrow's pointing, and flip it all back around to the way it was
	top_theta = start_theta - fortyfive
	bot_theta = pi - fortyfive - end_theta
	xoff1 = cos(top_theta) * taillen
	yoff1 = sin(top_theta) * taillen
	xoff2 = cos(bot_theta) * taillen
	yoff2 = sin(bot_theta) * taillen
	x1 = (rightx - xoff1) * xco
	x2 = (rightx - xoff2) * xco
	y1 = (topy - yoff1) * yco
	y2 = (topy - yoff2) * yco
	startx = leftx * xco
	starty = boty * yco
	endx = rightx * xco
	endy = topy * yco
	return [startx, starty, endx, endy], [x1, y1, endx, endy, x2, y2]

def get_quad_vertices(ox,
						oy,
						dx,
						dy,
						x1,
						y1,
						endx,
						endy,
						x2,
						y2,
						bgr,
						fgr,
						label_w=100,
						label_h=100):
	if dx > ox:
		if dy > oy:
			label_pos = x2 - label_w, dy - label_h
		elif dy < oy:
			label_pos = x1 - label_w, dy
		else:
			label_pos = x2 - label_w, y2 - label_h
	elif dx < ox:
		if dy < oy:
			label_pos = x2, y2
		elif dy > oy:
			label_pos = x1, y1 - label_h
		else:
			label_pos = x2, dy - label_h
	else:
		if dy > oy:
			label_pos = x2 - label_w, dy - label_h
		elif dy < oy:
			label_pos = x2, y2
		else:
			label_pos = dx, dy
	return {
		'shaft_bg': get_thin_rect_vertices(ox, oy, dx, dy, bgr),
		'shaft_fg': get_thin_rect_vertices(ox, oy, dx, dy, fgr),
		'left_head_bg': get_thin_rect_vertices(x1, y1, endx, endy, bgr),
		'right_head_bg': get_thin_rect_vertices(x2, y2, endx, endy, bgr),
		'left_head_fg': get_thin_rect_vertices(x1, y1, endx, endy, fgr),
		'right_head_fg': get_thin_rect_vertices(x2, y2, endx, endy, fgr),
		'label_pos': label_pos
	}

eight0s = tuple([0] * 8)

class GraphArrow:

	@property
	def slope(self) -> float:
		"""Return a float of the increase in y divided by the increase in x,
		both from left to right.

		Returns ``None`` when vertical.

		"""
		orig = self.origin
		dest = self.destination
		ox = orig.x
		oy = orig.y
		dx = dest.x
		dy = dest.y
		if oy == dy:
			return 0.
		elif ox == dx:
			return None
		else:
			rise = dy - oy
			run = dx - ox
			return rise / run

	@property
	def y_intercept(self) -> Optional[float]:
		"""Return my Y-intercept.

		I probably don't really hit the left edge of the window, but
		this is where I would, if I were long enough.

		"""
		orig = self.origin
		dest = self.destination
		(ox, oy) = orig.pos
		(dx, dy) = dest.pos
		denominator = dx - ox
		x_numerator = (dy - oy) * ox
		y_numerator = denominator * oy
		return (y_numerator - x_numerator), denominator

	@property
	def reciprocal(self) -> Optional["GraphArrow"]:
		"""The arrow connecting the same spots in the opposite direction"""
		if self.destination.name not in self.board.pred_arrow:
			return
		if self.origin.name not in self.board.pred_arrow[
			self.destination.name]:
			return
		return self.board.pred_arrow[self.destination.name][self.origin.name]

	@property
	def selected(self) -> bool:
		return self is self.board.app.selection

	@selected.setter
	def selected(self, b: bool):
		self.board.app.selection = self
		self.repoint(b)

	def __init__(self, *, board, origin, destination):
		self.board = board
		self.origin = origin
		self.destination = destination

	def collide_point(self, x: float, y: float) -> bool:
		return self.board.arrow_plane._colliders_map[
			self.origin.name, self.destination.name].collide_point(x, y)

	def pos_along(self, pct: float) -> Tuple[float, float]:
		"""Return coordinates for where a Pawn should be if it has travelled
		along ``pct`` of my length (between 0 and 1).

		Might get complex when I switch over to using beziers for
		arrows, but for now this is quite simple, using distance along
		a line segment.

		"""
		if pct < 0 or pct > 1:
			raise ValueError("Invalid portion")
		(ox, oy) = self.origin.center
		(dx, dy) = self.destination.center
		xdist = (dx - ox) * pct
		ydist = (dy - oy) * pct
		return ox + xdist, oy + ydist

	@mainthread
	def repoint(self, selected: bool = None) -> None:
		arrow_plane = self.board.arrow_plane
		fbo = arrow_plane._fbo
		fbo.bind()
		fbo.clear_buffer()
		shaft_points, head_points = get_points(self.origin, self.destination,
												arrow_plane.arrowhead_size)
		r = arrow_plane.arrow_width / 2
		if selected or self.selected:
			bg_scale = arrow_plane.bg_scale_selected
			bg_color = arrow_plane.bg_color_selected
			fg_color = arrow_plane.fg_color_selected
		else:
			bg_scale = arrow_plane.bg_scale_unselected
			bg_color = arrow_plane.bg_color_unselected
			fg_color = arrow_plane.fg_color_unselected
		plane = self.board.arrow_plane
		portal = self.board.character.portal[self.origin.name][
			self.destination.name]
		portal_text = str(portal.get(portal.get('_label_stat', None), ''))
		label_kwargs = dict(portal.get("label_kwargs", ()))
		if portal_text is not None:
			label_kwargs['text'] = portal_text
		try:
			label = self.board.arrow_plane._labels[self.origin.name][
				self.destination.name]
			label.text = portal_text
		except KeyError:
			label = self.board.arrow_plane.labels[self.origin.name][
				self.destination.name] = Label(**DEFAULT_ARROW_LABEL_KWARGS,
												**label_kwargs)
		if (self.origin.name,
			self.destination.name) in plane._instructions_map:
			verts = get_quad_vertices(*shaft_points, *head_points,
										r * bg_scale, r, *label.render())
			insts = self.board.arrow_plane._instructions_map[
				self.origin.name, self.destination.name]
			insts['color0'].rgba = bg_color
			insts['color1'].rgba = fg_color
			insts['shaft_bg'].points = verts['shaft_bg']
			insts['left_head_bg'].points = verts['left_head_bg']
			insts['right_head_bg'].points = verts['right_head_bg']
			insts['shaft_fg'].points = verts['shaft_fg']
			insts['left_head_fg'].points = verts['left_head_fg']
			insts['right_head_fg'].points = verts['right_head_fg']
			insts['label_rect'].pos = verts['label_pos']
			insts['label_rect'].size = label.render()
			label.refresh()
			insts['label_rect'].texture = label.texture
			plane._colliders_map[self.origin.name,
									self.destination.name] = Collide2DPoly(
										points=verts['shaft_bg'])
		else:
			plane._instructions_map[
				self.origin.name,
				self.destination.name] = insts = get_instructions(
					*shaft_points, *head_points, bg_color, fg_color,
					*label.render(), label)
			plane._colliders_map[
				self.origin.name,
				self.destination.name].points = Collide2DPoly(
					points=insts['shaft_bg'].points)
		myidx = plane._port_index[self.origin.name, self.destination.name]
		(ox, oy, dx, dy) = shaft_points
		plane._bot_left_corner_xs[myidx] = min((ox, dx))
		plane._bot_left_corner_ys[myidx] = min((oy, dy))
		plane._top_right_corner_xs[myidx] = max((ox, dx))
		plane._top_right_corner_ys[myidx] = max((oy, dy))
		fbo.release()
		fbo.ask_update()
		arrow_plane.canvas.ask_update()

def get_instructions(ox: float,
						oy: float,
						dx: float,
						dy: float,
						x1: float,
						y1: float,
						endx: float,
						endy: float,
						x2: float,
						y2: float,
						bgr: float,
						r: float,
						bg_color: Tuple[float, float, float, float],
						fg_color: Tuple[float, float, float, float],
						label_kwargs: dict = None,
						label: Label = None) -> dict:
	if label_kwargs is None:
		label_kwargs = {'text': '', **DEFAULT_ARROW_LABEL_KWARGS}
	else:
		label_kwargs = dict(label_kwargs)
		for k, v in DEFAULT_ARROW_LABEL_KWARGS.items():
			if k not in label_kwargs:
				label_kwargs[k] = v
	if label is None:
		label = Label(**label_kwargs)
	else:
		label.text = label_kwargs['text']
	text_size = label.render()
	quadverts = get_quad_vertices(ox, oy, dx, dy, x1, y1, endx, endy, x2, y2,
									bgr, r, *text_size)
	label.refresh()
	return {
		'color0':
		Color(rgba=bg_color),
		'shaft_bg':
		Quad(points=quadverts['shaft_bg']),
		'left_head_bg':
		Quad(points=quadverts['left_head_bg']),
		'right_head_bg':
		Quad(points=quadverts['right_head_bg']),
		'color1':
		Color(rgba=fg_color),
		'shaft_fg':
		Quad(points=quadverts['shaft_fg']),
		'left_head_fg':
		Quad(points=quadverts['left_head_fg']),
		'right_head_fg':
		Quad(points=quadverts['right_head_fg']),
		'label_rect':
		Rectangle(pos=quadverts['label_pos'],
					size=text_size,
					texture=label.texture),
		'label':
		label
	}

class GraphArrowWidget(Widget, GraphArrow):
	arrowhead_size = NumericProperty(10)
	arrow_width = NumericProperty(2)
	bg_scale = NumericProperty(5)
	bg_color = ListProperty([0.5, 0.5, 0.5, 0.5])
	fg_color = ListProperty([1.0, 1.0, 1.0, 1.0])

	def __init__(self, **kwargs):
		self._trigger_repoint = Clock.create_trigger(self.repoint)
		super().__init__(**kwargs)

	def on_origin(self, *args):
		if hasattr(self, '_origin_bind_uid'):
			self.unbind_uid(self._origin_bind_uid)
			del self._origin_bind_uid
		if not self.origin:
			return
		self._origin_bind_uid = self.origin.fbind('pos', self._trigger_repoint)

	def on_destination(self, *args):
		if hasattr(self, '_destination_bind_uid'):
			self.unbind_uid(self._destination_bind_uid)
			del self._destination_bind_uid
		if not self.destination:
			return
		self._destination_bind_uid = self.destination.fbind(
			'pos', self._trigger_repoint)

	def on_parent(self, *args):
		if not self.origin or not self.destination or not self.canvas:
			Clock.schedule_once(self.on_parent, 0)
			return
		self.canvas.clear()
		if self.parent is None:
			return
		shaft_points, head_points = get_points(self.origin, self.destination,
												self.arrowhead_size)
		with self.canvas:
			r = self.arrow_width / 2
			self._instructions = get_instructions(*shaft_points, *head_points,
													r * self.bg_scale, r,
													self.bg_color,
													self.fg_color)

	def repoint(self, *args):
		shaft_points, head_points = get_points(self.origin, self.destination,
												self.arrowhead_size)
		r = self.arrow_width / 2
		try:
			portal = self.board.character.portal[self.origin.name][
				self.destination.name]
			portal_text = str(portal.get(portal.get('_label_stat', None), ''))
		except (KeyError, AttributeError):
			portal_text = ''
		if hasattr(self, '_label'):
			label = self._label
			label.text = portal_text
		else:
			label = self._label = Label(text=portal_text)
		label_size = label.render()
		verts = get_quad_vertices(*shaft_points, *head_points,
									r * self.bg_scale, r, *label_size)
		insts = self._instructions
		insts['color0'].rgba = self.bg_color
		insts['color1'].rgba = self.fg_color
		insts['shaft_bg'].points = verts['shaft_bg']
		insts['left_head_bg'].points = verts['left_head_bg']
		insts['right_head_bg'].points = verts['right_head_bg']
		insts['shaft_fg'].points = verts['shaft_fg']
		insts['left_head_fg'].points = verts['left_head_fg']
		insts['right_head_fg'].points = verts['right_head_fg']
		insts['label_rect'].pos = verts['label_pos']
		insts['label_rect'].size = label_size
		label.refresh()
		insts['label_rect'].texture = label.texture

class ArrowPlane(Widget):
	data = ListProperty()
	arrowhead_size = NumericProperty(10)
	arrow_width = NumericProperty(2)
	bg_scale_unselected = NumericProperty(4)
	bg_scale_selected = NumericProperty(5)
	bg_color_selected = ListProperty([0.0, 1.0, 1.0, 1.0])
	bg_color_unselected = ListProperty([0.5, 0.5, 0.5, 0.5])
	fg_color_unselected = ListProperty([1.0, 1.0, 1.0, 1.0])
	fg_color_selected = ListProperty([1.0, 1.0, 1.0, 1.0])

	def __init__(self, **kwargs):
		self._labels = defaultdict(dict)
		self._trigger_redraw = Clock.create_trigger(self.redraw)
		self._redraw_bind_uid = self.fbind('data', self._trigger_redraw)
		self.bind(arrowhead_size=self._trigger_redraw)
		self._colliders_map = {}
		self._instructions_map = {}
		self._port_index = {}
		self._port_l = []
		self._bot_left_corner_ys = []
		self._bot_left_corner_xs = []
		self._top_right_corner_ys = []
		self._top_right_corner_xs = []
		super().__init__(**kwargs)

	def on_parent(self, *args):
		if not self.canvas:
			Clock.schedule_once(self.on_parent, 0)
			return
		with self.canvas:
			self._fbo = Fbo(size=self.size)
			self._translate = Translate(x=self.x, y=self.y)
			self._rectangle = Rectangle(size=self.size,
										texture=self._fbo.texture)
		self._trigger_redraw()

	def have_arrow(self, orig, dest):
		return (orig, dest) in self._port_index

	def redraw(self, *args):
		if not hasattr(self, '_rectangle'):
			self._trigger_redraw()
			return
		fbo = self._fbo
		fbo.bind()
		fbo.clear()
		fbo.clear_buffer()
		add = fbo.add
		r = self.arrow_width // 2
		bg_scale_selected = self.bg_scale_selected
		bg_color_unselected = self.bg_color_unselected
		fg_color_unselected = self.fg_color_unselected
		taillen = self.arrowhead_size
		points_map = get_points_multi(
			(datum['origspot'], datum['destspot'], taillen)
			for datum in self.data)
		port_l = []
		bot_left_corner_xs = []
		bot_left_corner_ys = []
		top_right_corner_xs = []
		top_right_corner_ys = []
		port_index = self._port_index
		colliders_map = self._colliders_map
		oxs = []
		oys = []
		dxs = []
		dys = []
		for ((ox, oy, dx, dy), _) in points_map.values():
			oxs.append(ox)
			oys.append(oy)
			dxs.append(dx)
			dys.append(dy)
		widths = np.abs(np.array(dxs) - np.array(oxs))
		heights = np.abs(np.array(dys) - np.array(oys))
		lengths = np.sqrt(np.square(widths) + np.square(heights))
		for (length, (port, ((ox, oy, dx, dy), (x1, y1, endx, endy, x2, y2))),
				datum) in zip(lengths, points_map.items(), self.data):
			if length < r:
				continue
			bgr = r * bg_scale_selected  # change for selectedness pls
			instructions = get_instructions(ox, oy, dx, dy, x1, y1, endx, endy,
											x2, y2, bgr, r,
											bg_color_unselected,
											fg_color_unselected,
											datum.get('label_kwargs', {}),
											self._labels[port[0]].get(port[1]))
			instructions['group'] = grp = InstructionGroup()
			grp.add(instructions['color0'])
			grp.add(instructions['shaft_bg'])
			grp.add(instructions['left_head_bg'])
			grp.add(instructions['right_head_bg'])
			grp.add(instructions['color1'])
			grp.add(instructions['shaft_fg'])
			grp.add(instructions['left_head_fg'])
			grp.add(instructions['right_head_fg'])
			grp.add(instructions['label_rect'])
			add(grp)
			self._instructions_map[port] = instructions
			self._labels[port[0]][port[1]] = instructions['label']
			if ox < dx:
				leftx = ox
				rightx = dx
			else:
				rightx = ox
				leftx = dx
			if oy < dy:
				boty = oy
				topy = dy
			else:
				boty = dy
				topy = oy
			port_index[port] = len(port_l)
			port_l.append(port)
			bot_left_corner_xs.append(leftx - bgr)
			bot_left_corner_ys.append(boty - bgr)
			top_right_corner_xs.append(rightx + bgr)
			top_right_corner_ys.append(topy + bgr)
			colliders_map[port] = Collide2DPoly(
				points=instructions['shaft_bg'].points)
		fbo.release()
		self.canvas.ask_update()
		self._port_l = port_l
		self._bot_left_corner_xs = np.array(bot_left_corner_xs)
		self._bot_left_corner_ys = np.array(bot_left_corner_ys)
		self._top_right_corner_xs = np.array(top_right_corner_xs)
		self._top_right_corner_ys = np.array(top_right_corner_ys)

	def add_new_portal(self, datum: dict) -> None:
		orig_spot = datum['origspot']
		dest_spot = datum['destspot']
		shaft_points, head_points = get_points(orig_spot, dest_spot,
												self.arrowhead_size)
		self.unbind_uid('data', self._redraw_bind_uid)
		self.data.append(datum)
		r = self.arrow_width / 2
		bgr = r * self.bg_scale_unselected
		instructions = self._instructions_map[
			orig_spot.name, dest_spot.name] = get_instructions(
				*shaft_points, *head_points, bgr, r,
				self.bg_color_unselected, self.fg_color_unselected,
				datum.get('label_kwargs',
							{}), self._labels[orig_spot.name][dest_spot.name])
		instructions['group'] = grp = InstructionGroup()
		grp.add(instructions['color0'])
		grp.add(instructions['shaft_bg'])
		grp.add(instructions['left_head_bg'])
		grp.add(instructions['right_head_bg'])
		grp.add(instructions['color1'])
		grp.add(instructions['shaft_fg'])
		grp.add(instructions['left_head_fg'])
		grp.add(instructions['right_head_fg'])
		grp.add(instructions['label'])
		self._labels[orig_spot.name][dest_spot.name] = instructions['label']
		self._fbo.add(grp)
		ox, oy, dx, dy = shaft_points
		if ox < dx:
			left_x = ox
			right_x = dx
		else:
			right_x = ox
			left_x = dx
		if oy < dy:
			bot_y = oy
			top_y = dy
		else:
			bot_y = dy
			top_y = oy
		self._port_index[orig_spot.name, dest_spot.name] = len(self._port_l)
		self._port_l.append((orig_spot.name, dest_spot.name))
		self._bot_left_corner_xs = np.array(
			list(self._bot_left_corner_xs) + [left_x - bgr])
		self._bot_left_corner_ys = np.array(
			list(self._bot_left_corner_ys) + [bot_y - bgr])
		self._top_right_corner_xs = np.array(
			list(self._top_right_corner_xs) + [right_x + bgr])
		self._top_right_corner_ys = np.array(
			list(self._top_right_corner_ys) + [top_y + bgr])
		self._colliders_map[orig_spot.name, dest_spot.name] = Collide2DPoly(
			points=instructions['shaft_bg'].points)
		self.canvas.ask_update()
		self._redraw_bind_uid = self.fbind('data', self._trigger_redraw)

	def remove_edge(self, orig, dest):
		self._fbo.bind()
		self._fbo.clear_buffer()
		self._fbo.remove(self._instructions_map[orig, dest]['group'])
		index = self._port_index[orig, dest]
		for port in self._port_l[index:]:
			self._port_index[port] -= 1
		del self._port_index[orig, dest]
		del self._instructions_map[orig, dest]
		del self._colliders_map[orig, dest]
		del self._port_l[index]
		for arr in ('_bot_left_corner_ys', '_bot_left_corner_xs',
					'_top_right_corner_ys', '_top_right_corner_xs'):
			dat = list(getattr(self, arr))
			del dat[index]
			setattr(self, arr, np.array(dat))
		self._fbo.release()
		self.canvas.ask_update()

	def update_portal_label(self, orig, dest, text):
		rect = self._instructions_map[orig, dest]['label']
		label = self._labels[orig][dest]
		label.text = text
		label.refresh()
		rect.texture = label.texture

	def iter_collided_edges(self, x: float, y: float) -> Iterator:
		x, y = map(float, (x, y))
		collider_map = self._colliders_map
		hits = (self._bot_left_corner_xs <= x) & (
			self._bot_left_corner_ys <= y
		) & (x <= self._top_right_corner_xs) & (y <= self._top_right_corner_ys)
		for port in map(itemgetter(0),
						filter(itemgetter(1), zip(self._port_l, hits))):
			if collider_map[port].collide_point(x, y):
				yield port

	def on_pos(self, *args):
		if not hasattr(self, '_translate'):
			return
		self._translate.x, self._translate.y = self.pos
		self.canvas.ask_update()

	def on_size(self, *args):
		if not hasattr(self, '_rectangle') or not hasattr(self, '_fbo'):
			return
		self._rectangle.size = self._fbo.size = self.size
		self.redraw()


import numpy as np
from cbfssm.datasets import VoliroFlipDS
from cbfssm.training import Trainer
from cbfssm.outputs import OutputsVoliro
from cbfssm.model import Voliro

#
# Config
#
root_dir = "run_output/voliro"
# dataset
ds_sel = VoliroFlipDS
seq_len = 64
seq_stride = 50
# model
model_sel = Voliro
model_config = {
    # dataset
    'ds': ds_sel,
    'batch_size': 16,
    'shuffle': 10000,
    # method
    'ind_pnt_num': 20,
    'samples': 20,
    'learning_rate': 0.01,
    'loglik_factor': np.asarray([20., 0., 0.2 * 20 * 50]),
    'n_beta': [10., 2., 10.],
    'l_beta': [1., 10., 10.],
    # variables init state
    'zeta_pos': 2.,
    'zeta_mean': 0.05 ** 2,
    'zeta_var': 0.01 ** 2,
    'gp_var': 0.5 ** 2,
    'gp_len': 5.,
    'var_x': np.asarray([0.02, 0.02, 0.02,
                         0.02, 0.02, 0.02, 0.02,
                         0.2, 0.2, 0.2,
                         0.2, 0.2, 0.2]) ** 2,
    'var_y': np.asarray([0.02, 0.02, 0.02,
                         0.02, 0.02, 0.02, 0.02,
                         0.2, 0.2, 0.2,
                         0.2, 0.2, 0.2]) ** 2,
    'var_z': np.asarray([0.02] * 6)
}
# training
train = True
epochs = 2000
# evaluation
output_sel = OutputsVoliro

#
# Run
#
# load
outputs = output_sel(root_dir)
ds = ds_sel(seq_len, seq_stride)
outputs.set_ds(ds)
model = model_sel(model_config)
outputs.set_model(model, root_dir)
# train
if train:
    trainer = Trainer(model, root_dir)
    trainer.train(ds, epochs)
    outputs.set_trainer(trainer)
# evaluate
outputs.create_all()


from time import sleep

from django.core.management.base import BaseCommand

from books.googlebooks import get_book_info_from_api
from books.models import Book, Category

class Command(BaseCommand):
    help = 'Add categories to existing books'

    def handle(self, *args, **options):
        books = Book.objects.all()
        for book in books:
            self.stdout.write(f"Adding categories for book {book.bookid} ({book.title})")
            if book.categories.count() > 0:
                self.stderr.write("book already has categories, skipping")
                continue
            try:
                get_book_info_from_api(book.bookid)
            except KeyError:
                self.stderr.write("Cannot get book info from Google Books API, skipping")
            sleep(0.5)  # not sure about API rates