import os
import random

import torch
from torch import nn
from torch import optim, hub
from torch.utils.data import DataLoader
from torchvision.transforms import transforms

from config import Config
from datagen import CasiaDataset
from models.resnet import resnet18
from tools.train_utils import parse_args, train, validate

CONFIG = Config()
hub.set_dir(CONFIG['TORCH_HOME'])

def main():
    args = parse_args()
    print(args)

    os.environ["CUDA_VISIBLE_DEVICES"] = str(args.gpu)
    random.seed(args.seed)
    torch.manual_seed(args.seed)
    torch.cuda.manual_seed_all(args.seed)

    print("Initializing Networks")
    model = resnet18(pretrained=True)
    model.cuda()
    optimizer = optim.Adam(model.parameters())
    criterion = nn.CrossEntropyLoss().cuda()

    print("Initializing Data Loader")
    transform = transforms.Compose([
        transforms.ToPILImage(),
        transforms.Resize((224, 224)),
        # transforms.RandomHorizontalFlip(),
        # transforms.RandomRotation((0, 20)),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])
    train_data = CasiaDataset(data_root=args.data_dir, mode='train', transform=transform)
    train_loader = DataLoader(dataset=train_data, batch_size=args.batch_size)
    # data_batch = next(iter(train_loader))
    # imshow(data_batch)

    val_data = CasiaDataset(data_root=args.data_dir, mode='validation', transform=transform)
    val_loader = DataLoader(dataset=val_data, batch_size=args.batch_size)

    print("Start Training")
    best_acc1 = 0.
    for epoch in range(1, args.epochs + 1):
        train(train_loader, model, optimizer, criterion, epoch, args)
        acc1 = validate(val_loader, model, criterion, args)

        best_acc1 = max(acc1, best_acc1)

        if epoch == args.epochs:
            print('Save model')
            torch.save({
                'epoch': epoch,
                'arch': args.arch,
                'state_dict': model.state_dict(),
                'best_acc1': best_acc1,
                'optimizer': optimizer.state_dict(),
            }, os.path.join('weights', '{}_{}.pt'.format(args.arch, args.prefix)))

if __name__ == '__main__':
    """
    python train.py --data-dir /home/shaohua/data/Datasets/Face_Anti_Spoofing/mini-casia --arch resnet18 --epoch 10 --batch-size 16
    """
    main()


import json
import requests
from abc import ABC, abstractmethod
from bs4 import BeautifulSoup as Bs

class Parsing(ABC):
    html = None

    def __init__(self, url):
        self.url = url

    def get_html(self):
        response = requests.get(self.url)

        if response.status_code == 200:
            self.html = Bs(response.text, features='lxml')
        else:
            raise RuntimeError(f'status code: {response.status_code}; {response.text}')

    @abstractmethod
    def parsing(self):
        ...

    def save(self, file_name):
        with open(file_name, 'w') as file:
            json.dump(self.parsing(), file, indent=4, ensure_ascii=False)


from fastapi import Request, HTTPException
from fastapi.security import HTTPBearer,HTTPAuthorizationCredentials

from auth.authenticateHandler import decodeJWT

class JWTBearer(HTTPBearer):
    def __init__(self,auto_error:bool = True):
        super(JWTBearer,self).__init__(auto_error=auto_error)

    async def __call__(self,request:Request):
        creadentials:HTTPAuthorizationCredentials = await super(JWTBearer,self).__call__(request)
        if creadentials:
            if not creadentials.scheme == "Bearer":
                raise HTTPException(status_code=403,detail={})
            if not self.verifyJWT(creadentials.credentials):
                raise HTTPException(status_code=403,detail="Invalid token or expired token")
            return creadentials.credentials
        else:
            raise HTTPException(status_code=403,detail="Invalid Authorization token")

    def verifyJWT(self,JWTToken:str) -> bool:
        isTokenValid:bool = False
        try:
            payload = decodeJWT(JWTToken)
        except:
            payload = None
        if payload:
            isTokenValid = True
        return isTokenValid


import sys
import logging
from utils.settings import data

# Set the logging settings
formatting = '%(asctime)s [%(levelname)s]: %(message)s'
if data.get('logging') not in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']:
    logging.error("Check the JSON configuration logging option. Only options allowed:"
                  "\nDEBUG"
                  "\nINFO"
                  "\nWARNING"
                  "\nERROR"
                  "\nCRITICAL")
    sys.exit(1)
level = logging.getLevelName(data.get('logging'))
logging.basicConfig(format=formatting, level=level)


import numpy as np
import os
from tqdm import tqdm
import math
from os import listdir
from os.path import isfile, join
import sys
import argparse
import random

MAX = 5500000

parser = argparse.ArgumentParser(description='Runs sample data')

parser.add_argument('-n', dest="n", type=int,
                    required=True,
                    help='N samples to extract\n'
                         '(type: int)\n'
                         '(REQUIRED)')

parser.add_argument('-src', dest="src", type=str,
                    required=True,
                    help='Path to source file\n'
                         '(type: str)\n'
                         '(REQUIRED)')

parser.add_argument('-tgt', dest="tgt", type=str,
                    required=True,
                    help='Path to target file\n'
                         '(type: str)\n'
                         '(REQUIRED)')

args = parser.parse_args()

N = args.n

src = args.src
tgt = args.tgt

sample_dir = "sample"
try:
  os.mkdir(sample_dir)
except:
  print(sample_dir+" dir already exist")

data = []
with open(src, "r") as s_file, open(tgt, "r") as t_file:
  for s, t in tqdm(zip(s_file, t_file)):
    data.append([s.rstrip(), t.rstrip()])
    #tgt_data.append(t.rstrip())

res = random.sample(data, N)

print(len(res))

name_s = src.split("/")[-1]
name_t = tgt.split("/")[-1]
with open(sample_dir+"/"+name_s.split(".")[0]+"_"+str(N)+"."+name_s.split(".")[1], "w") as s_file, open(sample_dir+"/"+name_t.split(".")[0]+"_"+str(N)+"."+name_t.split(".")[1], "w") as t_file:
  for s, t in res:
    s_file.write(s+'\n')
    t_file.write(t+'\n')


import platform

import gym
from allenact.algorithms.onpolicy_sync.losses import PPO
from allenact.algorithms.onpolicy_sync.losses.ppo import PPOConfig
from allenact.utils.experiment_utils import TrainingPipeline, Builder, PipelineStage, LinearDecay
from allenact_plugins.ithor_plugin.ithor_sensors import RGBSensorThor
from torch import nn, optim
from torch.optim.lr_scheduler import LambdaLR

from ithor_arm.bring_object_sensors import TargetObjectBBox, TargetLocationBBox
from ithor_arm.bring_object_task_samplers import DiverseBringObjectTaskSampler, OnlyAppleBreadBringObjectTaskSampler
from ithor_arm.ithor_arm_constants import ENV_ARGS, TRAIN_OBJECTS, TEST_OBJECTS
from ithor_arm.ithor_arm_sensors import (
    InitialAgentArmToObjectSensor,
    InitialObjectToGoalSensor,
    PickedUpObjSensor,
    DepthSensorThor, RelativeAgentArmToObjectSensor, RelativeObjectToGoalSensor,
)
from manipulathor_baselines.bring_object_baselines.experiments.bring_object_mixin_ddppo import BringObjectMixInPPOConfig
from manipulathor_baselines.bring_object_baselines.experiments.bring_object_mixin_simplegru import BringObjectMixInSimpleGRUConfig
from manipulathor_baselines.bring_object_baselines.experiments.ithor.bring_object_ithor_base import BringObjectiThorBaseConfig
from manipulathor_baselines.bring_object_baselines.losses.bring_object_losses import PredictBoxBCELsss
from manipulathor_baselines.bring_object_baselines.models.pickup_object_with_mask_model import PickUpWMaskBaselineActorCritic
from manipulathor_baselines.bring_object_baselines.models.temp_pred_box_bring_object import PredictBBoxwTFBringObjectModel
from manipulathor_baselines.bring_object_baselines.models.small_bring_object_with_mask_model import SmallBringObjectWMaskDepthBaselineActorCritic
from manipulathor_baselines.bring_object_baselines.models.small_depth_pickup_object_with_mask_model import SmallPickUpWMaskDepthBaselineActorCritic
from manipulathor_utils.debugger_util import ForkedPdb

class AttentionBBoxBringObject(
    BringObjectiThorBaseConfig,
    BringObjectMixInPPOConfig,
    BringObjectMixInSimpleGRUConfig,
):
    """An Object Navigation experiment configuration in iThor with RGB
    input."""

    SENSORS = [
        DepthSensorThor(
            height=BringObjectiThorBaseConfig.SCREEN_SIZE,
            width=BringObjectiThorBaseConfig.SCREEN_SIZE,
            use_normalization=True,
            uuid="depth_lowres",
        ),
        RGBSensorThor(
            height=BringObjectiThorBaseConfig.SCREEN_SIZE,
            width=BringObjectiThorBaseConfig.SCREEN_SIZE,
            use_resnet_normalization=True,
            uuid="rgb_lowres",
        ),
        PickedUpObjSensor(),
        TargetObjectBBox(),
        TargetLocationBBox(),
    ]

    MAX_STEPS = 200
    TASK_SAMPLER = OnlyAppleBreadBringObjectTaskSampler
    NUM_PROCESSES = 40
    # TRAIN_SCENES = ['FloorPlan1_physics']
    # TEST_SCENES = ['FloorPlan1_physics']
    OBJECT_TYPES = TRAIN_OBJECTS + TEST_OBJECTS

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

        assert (
            self.CAMERA_WIDTH == 224
            and self.CAMERA_HEIGHT == 224
            and self.VISIBILITY_DISTANCE == 1
            and self.STEP_SIZE == 0.25
        )
        self.ENV_ARGS = {**ENV_ARGS, "renderDepthImage": True}

    @classmethod
    def create_model(cls, **kwargs) -> nn.Module:
        return PredictBBoxwTFBringObjectModel(
            action_space=gym.spaces.Discrete(
                len(cls.TASK_SAMPLER._TASK_TYPE.class_action_names())
            ),
            observation_space=kwargs["sensor_preprocessor_graph"].observation_spaces,
            hidden_size=512,
        )

    def training_pipeline(self, **kwargs):
        print('resolve todo')
        ForkedPdb().set_trace()
        ppo_steps = int(300000000)
        lr = 3e-4
        num_mini_batch = 1
        update_repeats = 4
        num_steps = 128 #self.MAX_STEPS
        #
        save_interval = 500000  # from 50k
        log_interval = 1000
        gamma = 0.99
        use_gae = True
        gae_lambda = 0.95
        max_grad_norm = 0.5
        return TrainingPipeline(
            save_interval=save_interval,
            metric_accumulate_interval=log_interval,
            optimizer_builder=Builder(optim.Adam, dict(lr=lr)),
            num_mini_batch=num_mini_batch,
            update_repeats=update_repeats,
            max_grad_norm=max_grad_norm,
            num_steps=num_steps,
            named_losses={"ppo_loss": PPO(**PPOConfig), "pred_box_bce": PredictBoxBCELsss()},
            gamma=gamma,
            use_gae=use_gae,
            gae_lambda=gae_lambda,
            advance_scene_rollout_period=self.ADVANCE_SCENE_ROLLOUT_PERIOD,
            pipeline_stages=[
                # PipelineStage(loss_names=["ppo_loss"], max_stage_steps=ppo_steps)
                PipelineStage(
                    loss_names=["ppo_loss", "pred_box_bce"],
                    loss_weights=[1.0, 1.0], #LATER_TODO how is this?
                    max_stage_steps=ppo_steps,
                )
            ],

            lr_scheduler_builder=Builder(
                LambdaLR, {"lr_lambda": LinearDecay(steps=ppo_steps)}
            ),
        )
    @classmethod
    def tag(cls):
        return cls.__name__


from pathlib import Path

from setuptools import setup, find_packages

root = Path(__file__).parent.absolute()
cortex_client_dir = root.parent.parent / "python" / "client"

if not cortex_client_dir.exists():
    raise ModuleNotFoundError(f"cortex client not found in {cortex_client_dir}")

setup(
    name="e2e",
    version="master",  # CORTEX_VERSION
    packages=find_packages(exclude=["tests"]),
    url="https://github.com/cortexlabs/cortex",
    license="Apache License 2.0",
    python_requires=">=3.6",
    install_requires=[
        "requests==2.24.0",
        "jsonschema==3.2.0",
        "pytest==6.1.*",
        "pytest-print==0.2.1",
        "python-dotenv==0.15.0",
        "pyyaml>=5.3.1",
        "boto3>=1.14.53",
        "cortex",
    ],
    dependency_links=[f"file://{cortex_client_dir}#egg=cortex"],
    author="Cortex Labs",
    author_email="dev@cortexlabs.com",
    description="Cortex E2E tests package",
)


from requests import post, get
from utils.logger import register
from time import sleep
from json import loads

def balance(log, token, channel_id, timeout, logging, ID):
    request = post(f"https://discord.com/api/v8/channels/{channel_id}/messages", headers={"authorization": token}, data={"content": "pls bal"})

    if request.status_code != 200 and logging["warning"]:
        register(log, "WARNING", f"Failed to send command `pls bal`. Status code: {request.status_code} (expected 200). Aborting command.")
        return

    if logging["debug"]:
        register(log, "DEBUG", "Successfully sent command `pls bal`.")

    latest_message = None

    for _ in range(0, timeout):
        sleep(1)

        request = get(f"https://discord.com/api/v8/channels/{channel_id}/messages", headers={"authorization": token})

        if request.status_code != 200:
            continue

        latest_message = loads(request.text)[0]

        if latest_message["author"]["id"] == "270904126974590976" and latest_message["referenced_message"]["author"]["id"] == ID:
            if logging["debug"]:
                register(log, "DEBUG", "Got Dank Memer's response to command `pls bal`.")
            break
        else:
            continue

    if latest_message is None or latest_message["author"]["id"] != "270904126974590976":
        if logging["warning"]:
            register(log, "WARNING", f"Timeout exceeded for response from Dank Memer ({timeout} second(s)). Aborting command.")
        return

    return [True, latest_message]


import csv
from datetime import datetime

import matplotlib.pyplot as plt

filename = 'data/sitka_weather_2018_simple.csv'
with open(filename) as f:
	reader = csv.reader(f)
	header_row = next(reader)

	#Gather data for dates and daily rainfall amounts.
	dates, rains = [], []
	for row in reader:
		current_date = datetime.strptime(row[2], '%Y-%m-%d')
		rain = float(row[3])
		dates.append(current_date)
		rains.append(rain)

# Plot the daily rainfall.
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.plot(dates, rains, c='blue')

# Format plot
plt.title("Daily rainfall totals - 2018", fontsize=24)
plt.xlabel('', fontsize=16)
fig.autofmt_xdate()
plt.ylabel("Daily Rain (Inches)", fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)

plt.show()


from abc import ABC
import dearpygui.dearpygui as dpg
from pyneurode.processor_node.Message import Message
import numpy as np
import shortuuid
from pyneurode.RingBuffer.RingBuffer import RingBuffer
from typing import List, Union
from .Visualizer import Visualizer

class AnalogVisualizer(Visualizer):
    '''
    Display a analog value
    '''
    def __init__(self, name:str,  buffer_length:int = 500, scale = 1, time_scale=None) -> None:
        super().__init__(name)
        self.plot_data = (np.arange(buffer_length), np.zeros((buffer_length,)))
        self.name = name #unique identying string for the visualizer
        self.data_count = 0
        self.plot_data_tag = self.name+'_plot_data'
        self.buffer = None
        self.buffer_length = buffer_length
        self.first_run = True
        self.scale = scale
        self.time_scale = time_scale # time period of each data point
        self.time_cursor = 'time_cursor'
        self.data_idx = None

    def init_gui(self):
        with dpg.window(label=self.name, width=800, height=500, tag=self.name):
            with dpg.plot(label='Plot', height=-1, width=-1) as plot_id:
                self.x_axis = dpg.add_plot_axis(dpg.mvXAxis, label="")
                self.y_axis = dpg.add_plot_axis(dpg.mvYAxis, label="")
                dpg.set_axis_limits_auto(self.x_axis)
                dpg.set_axis_limits_auto(self.y_axis)

    def update(self, messages: List[Message]):
        # message data format time x channel
        if self.buffer is None:
            self.buffer = RingBuffer((self.buffer_length, messages[0].data.shape[1]))

        for m in messages:
            if m.data.shape[0] > self.buffer.length:
                #if the input data is too large, resize the data buffer
                self.buffer = RingBuffer((m.data.shape[0]*2, m.data.shape[1]))
            else:
                self.buffer.write(m.data)

        ####
        # update the plot

        # Re-arrange the data to be suitable for plotting
        ydata = self.buffer.buffer.T.copy()
        shift = np.arange(ydata.shape[0])[None,].T * self.scale
        ydata += shift #shift the data for different channel

        ydata = ydata.tolist()

        xdata = np.arange(len(ydata[0]))
        if self.time_scale:
            xdata = xdata *self.time_scale

        xdata = xdata.tolist()

        series_name = [f'{i}_{self.name}' for i in range(len(ydata))]

        if self.first_run:
            self.first_run = False
            # print(series_name)
            for i in range(len(ydata)):
                dpg.add_line_series(xdata, ydata[i], label=series_name[i], parent=self.y_axis, tag=series_name[i])
                dpg.fit_axis_data(self.y_axis)
                dpg.fit_axis_data(self.x_axis)

            # dpg.add_vline_series([1.0], parent = self.y_axis, tag=self.time_cursor)
        else:
            for i in range(len(ydata)):
                dpg.set_value(series_name[i],(xdata,ydata[i])) #must be called in main thread

        # print(message)
        # self.plot_data[1][self.data_count%500]=message.data[0]
        # self.data_count += 1

        # dpg.set_value(self.plot_data_tag, self.plot_data)


import typing

from nion.ui import Declarative
from nion.ui import Widgets
from nion.utils import Model

class Handler(Declarative.Handler):
    def __init__(self) -> None:
        super().__init__()
        self.expanded_model0 = Model.PropertyModel(False)
        self.expanded_model1 = Model.PropertyModel(True)
        self.expanded_model2 = Model.PropertyModel(False)
        self.section: typing.Optional[Widgets.SectionWidget] = None

    def switch3(self, widget: Declarative.UIWidget) -> None:
        self.expanded_model0.value = False
        self.expanded_model1.value = False
        self.expanded_model2.value = True
        if self.section:
            self.section.title = "Tres"

def construct_ui(u: Declarative.DeclarativeUI) -> Declarative.UIDescription:

    def create_section(tab_label: str, text: str, check_text: str, button_text: str, index: int) -> Declarative.UIDescription:
        label = u.create_label(text=text)
        check_box = u.create_check_box(text=check_text)
        button = u.create_push_button(text=button_text)
        return u.create_section(u.create_column(label, check_box, button, spacing=8, margin=4), title=tab_label, expanded=f"@binding(expanded_model{index}.value)", name="section")

    section0 = create_section("First", "ONE", "Check 1", "Push ONE", 0)
    section1 = create_section("Second", "TWO", "Check 2", "Push TWO", 1)
    section2 = create_section("Third", "THREE", "Check 3", "Push THREE", 2)

    button = u.create_push_button(text="3", on_clicked="switch3")

    return u.create_column(section0, section1, section2, button)


import datetime
import telebot
from data.config import TELEGRAM_TOKEN, CHAT_GPT_TOKEN
from app.chatGPT.main import ChatGPT
from tools.sqlSetting import get_access_rights, clean_user_status, get_user_status
from app.chatGPT.sqlChatGPT import get_dict_bots_for_users
from tools.apps_getter import get_apps_list
from requests.exceptions import ReadTimeout
from tools.debuger import debug_log

def do_func(function, arg=None):
    try:
        if arg is None:
            function()
        else:
            function(arg)
    except ReadTimeout:
        debug_log(debug_mode=True, text="Нет соединения с интернетом...", color="red")

class TelegramBot:

    def __init__(self, telegram_token=TELEGRAM_TOKEN, openai_token=CHAT_GPT_TOKEN):
        self.bot = telebot.TeleBot(telegram_token)
        self.name = {}
        self.apps_list = get_apps_list()
        self.chat_bot_dict = {}
        chat_bot_dict = get_dict_bots_for_users()
        for user_id in chat_bot_dict:
            self.chat_bot_dict[user_id] = ChatGPT(user_id, openai_token)

    def logger(self, _message):
        """Shows the message data in the console \n
        Показывает данные сообщения из телеграмма в консоли"""
        name = self.name.get(_message.from_user.id)
        if name is None:
            name = get_user_status(_message.from_user.id)["user_name"]
            if name is None:
                name = _message.from_user.first_name
        print(
            f"{datetime.datetime.now().strftime('%d-%m-%Y %H:%M:%S')} "
            f"{_message.from_user.id} "
            f"{name}: "
            f"{_message.text}"
        )

    def is_it_app_check(self, message):
        """Проверяет, является ли текст активатором приложения."""
        _text = message.text
        for app in self.apps_list:
            if _text in app.activate:
                return app

    def user_access_to_app_check(self, user_id: int, app_name: str):
        """Checks the user's permissions to use the app \n
        Проверяет права юзера на использование команды"""
        return get_access_rights(user_id=user_id, app_name=app_name)

    def standard_command(self, message, app):
        """Executes standard commands from the app folder \n
        Выполняет стандартную команду из папки app"""
        user_id = message.from_user.id
        print(f"standard_command: app = {app}")
        if self.user_access_to_app_check(user_id=user_id, app_name=app.name):
            app.command_run(message, self.bot)

        else:
            self.bot.send_message(message.from_user.id, "У вас нет прав на использование данной команды.")
            clean_user_status(user_id=user_id)

    def standard_command_continuation(self, message, app_name):
        """Продолжает работу app"""
        user_id = message.from_user.id
        app = ""
        for _app in self.apps_list:
            if _app.name == app_name:
                app = _app
                break

        if self.user_access_to_app_check(user_id=user_id, app_name=app.name):
            app.command_run(message, self.bot)

        else:
            self.bot.send_message(message.from_user.id, f"id: {user_id}. У вас нет прав на использование данной команды.")
            clean_user_status(user_id=user_id)

    def command_handler(self, message):
        """Defines and executes a standard or ChatGPT app \n
        Определяет и выполняет стандартную или ChatGPT команду"""
        user_id = message.from_user.id
        user_status = get_user_status(user_id=user_id)
        if user_status["activated_app"] == "":
            app = self.is_it_app_check(message)
            if app is not None:
                self.standard_command(message, app)  # Тут скрипт команды
            else:
                if get_access_rights(user_id=user_id, app_name="chatGPT"):
                    self.bot.send_message(user_id,
                                          self.chat_bot_dict[user_id].response(message),
                                          reply_markup=telebot.types.ReplyKeyboardRemove())  # тут ChatGPT
                else:
                    self.bot.send_message(user_id, f"id: {user_id}. Вы не имеете прав для использования ChatGPT.",
                                          reply_markup=telebot.types.ReplyKeyboardRemove())
        else:
            self.standard_command_continuation(message, user_status["activated_app"])

    def run(self):
        """Runs the main logic"""

        @self.bot.message_handler(content_types=['text'])
        def get_text_messages(message):

            do_func(self.logger, message)
            do_func(self.command_handler, message)

        self.bot.polling(none_stop=True, interval=0)

def return_exemplar():
    return TelegramBot()

if __name__ == "__main__":
    telegramBot = TelegramBot()
    telegramBot.run()


#! /usr/bin/python

import serial
import os
from time import sleep, time
import threading
from pyrow import pyrow
from pyrow.pyrow import PyErg
import json
import numpy as np
from VideoControl import VideoControl
import os
run_thread = True

all_data = list()

cnt_rot = 0
cnt_trans = 0
cnt_erg = 0
cnt_image = 0

do_beep = False

vc = VideoControl()

# seq_name = input("name for sequence: ")
# print(seq_name)

#
# t = int(time())
# run_name = "%s_%i" % (seq_name, t)
# folder = '/tmp/%s/' % run_name
# os.mkdir(folder)

min_gripper = 0
current_gripper = 0
gripper_front_pos = 0

def RotationThread():
    global cnt_rot, current_gripper, min_gripper, gripper_front_pos
    name = '/dev/serial/by-id/usb-SEGGER_J-Link_000599005480-if00'
    ser = serial.Serial(name, baudrate=115200)
    print(ser.is_open)

    rots = list()

    while run_thread:
        data = ser.readline().strip()
        if data:
            s = str(data)[2:][:-1]
            # print(s)
            spl = s.split(':')
            if len(spl) != 3:
                print("Could not parse " + s)
                continue

            try:
                speed, angle, rotations = map(float, spl)
            except ValueError as e:
                print(e)
                continue

            if angle < 0:
                angle += 360

            rot = angle + rotations*360

            rot_m = rot*0.00246219 * 100
            cnt_rot += 1
            if cnt_rot % 10 != 0:
                continue
                print("%.2f" % (rot_m))
            # continue

            current_gripper = rot_m
            rots.append(rot_m)

            if len(rots) < 5:
                continue

            a = rots[-3]
            b = rots[-2]
            c = rots[-1]

            # print("%.2f, %.2f, %.2f" % (a, b, c))

            if a < b and b > c and abs(a-b) > 0.04:
                print ("XXXXXXXXXXXX MAXIMUM DETECTED at %i" % int(time()))
                # print("%.2f < %.2f > %.2f" % (a, b, c))
                min_gripper = c
                if do_beep:
                    os.system("beep -f 1000 -l 50")

            if a > b and b < c and abs(a - b) > 0.04:
                print("000000000000000000 Minimum at %i: %.1f" % (int(time()), c))
                # print("%.2f < %.2f > %.2f" % (a, b, c))
                gripper_front_pos = c
                if do_beep:
                    os.system("beep -f 1000 -l 50")

            # print(a, b, c)

    ser.close()

def TranslationThread():
    global cnt_trans, current_gripper, min_gripper
    name = '/dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0'
    ser = serial.Serial(name, baudrate=230400)

    back = False

    all_trans = list()
    last_stamp = None
    stop_pos = 0
    stop_stamp = None

    front_stop = False
    front_stop_pos = 0
    front_stop_stamp = 0

    while run_thread:
        data = ser.readline()
        cnt_trans += 1
        if data:
            s = str(data)[2:][:-5]
            try:
                trans = float(s)
            except ValueError:
                continue

            trans = 0.03*trans/10 + 20
            if cnt_trans % 20 != 0:
                continue

            # print(trans)

            all_trans.append(trans)

            if len(all_trans) < 5:
                continue

            a = all_trans[-3]
            b = all_trans[-2]
            c = all_trans[-1]

            # print("%.2f, %.2f, %.2f" % (a, b, c))
            # print(c)
            v = c-b
            if c < 50:
                if back: # already stooped, waiting for acceleration:
                    # print("back: v: %.1f" % v)
                    if c > stop_pos + 5:  #v > 0.1:
                        dt_ms = (time()-stop_stamp)*1000.0
                        print("Moving FORWARD AGAIN after %.0f ms" % dt_ms)

                        gripper_dist = min_gripper - current_gripper
                        print("Gripper moved: %.1f (min: %.1f, current: %.1f" % (gripper_dist, min_gripper, current_gripper))
                        if min_gripper > 0 and gripper_dist < 500:
                            # os.system("beep -f 20 -l 300")
                            vc._say("Arms away")
                            # os.system("beep -f 200 -l 100")
                            # os.system("beep -f 100 -l 100")
                        if do_beep:
                            os.system("beep -f 900 -l 100")
                        back = False
                else:
                    # Not yet stopped:
                    # print("front yet? %.2f" % v)
                    # print()
                    if 0 > v > -0.5:
                        print("XXXXXXXXXXXXXXXXXXx BACKWARDS, STOPPING")
                        if do_beep:
                            os.system("beep -f 600 -l 100")
                        stop_pos = c
                        stop_stamp = time()
                        back = True

            # shooting the slide:
            if front_stop_pos > 0:
                # we are on the way back:
                if front_stop_pos - c > 20:
                    gripper_travel = (current_gripper - gripper_front_pos)/10.0  # to cm
                    print("00000000000000000000000000000000000000000000000000 Grpper travel: %i" % (int(gripper_travel)))
                    front_stop_pos = 0
                    if gripper_travel < 12:
                        vc._say("slide!")

            if c > 50:
                # print(v)

                if not front_stop:
                    if 0 < v < 1.0:
                        print("Front Stop")
                        front_stop = True
                        front_stop_pos = c
                        front_stop_stamp = time()
                        if do_beep:
                            os.system("beep -f 200 -l 100")
                else:
                    # we stopped, waiting for moving back:
                    if c < front_stop_pos - 3:
                        dt = (time()-front_stop_stamp)*1000.0
                        print("Moving back after %.1f ms" % dt)
                        front_stop = False
                        if do_beep:
                            os.system("beep -f 700 -l 100")

                #     if last_stamp and time()-last_stamp < 0.6:
                #         print("DT", time()-last_stamp)
                #         continue
                #
                #     print("Auslage")
                #     last_stamp = time()
                #     os.system("beep -f 200 -l 200")

            # print("%.1f" % (c-b))
            # if a < b and b > c and abs(a-b) > 0.04:
            #     print ("XXXXXXXXXXXX MAXIMUM DETECTED at %i" % int(time()))
            #     print("%.2f < %.2f > %.2f" % (a, b, c))
            #     os.system("beep -f 200 -l 200")
            #
            # if a > b and b < c and abs(a - b) > 0.04:
            #     print("000000000000000000 at %i" % int(time()))
            #     print("%.2f < %.2f > %.2f" % (a, b, c))
            #     os.system("beep -f 700 -l 200")

            # now = time()
            # msg = "T, %f, %.0f" % (now, trans)
            # all_data.append([now, msg])
            cnt_trans +=1

def ErgThread():
    global cnt_erg
    ergs = list(pyrow.find())

    erg = PyErg(ergs[0])

    while run_thread:
        erg_dict = erg.get_monitor(forceplot=True)
        now = time()
        msg = "E, %f, %s" % (now, json.dumps(erg_dict))
        all_data.append([now, msg])
        cnt_erg += 1

def CameraThread():
    global cnt_image
    cap = cv2.VideoCapture(1)
    while run_thread:
        # Capture frame-by-frame
        ret, frame = cap.read()
        t = time()
        img_path = folder + 'img_%f.png' % t
        cnt_image += 1

        cv2.imwrite(img_path, frame)

threads = list()
threads.append(threading.Thread(target=RotationThread, daemon=True))
threads.append(threading.Thread(target=TranslationThread, daemon=True))
# threads.append(threading.Thread(target=ErgThread, daemon=True))
# threads.append(threading.Thread(target=CameraThread, daemon=True))
#
for t in threads:
    t.start()
#
while True:
    sleep(40)
run_thread = False
#
# print("Rot: %i, Trans: %i, Erg: %i, Images: %i" % (cnt_rot, cnt_trans, cnt_erg, cnt_image))
#
# all_data = sorted(all_data, key=lambda d: d[0])
#
#
#
# print("Writing to %s" % folder)
#
# f_r = open(folder + 'rotation.txt', 'w')
# f_t = open(folder + 'translation.txt' , 'w')
# f_e = open(folder + 'ergo.txt', 'w')
#
# f_merge = open(folder + 'all_%s.txt' % run_name, 'w')
#
# for m in all_data:
#     msg = m[1]
#     f_merge.write(msg+'\n')
#     if msg[0] == 'R':
#         f_r.write(msg+'\n')
#     if msg[0] == 'T':
#         f_t.write(msg + '\n')
#     if msg[0] == 'E':
#         f_e.write(msg + '\n')
#     f_merge.write(msg+'\n')
#
# f_r.close()
# f_t.close()
# f_e.close()


from enum import Enum
from protocolo import Subcamada
import crc

# usando como base coisas desenvolvidas nos semestres anteriores
class Enquadramento(Subcamada):

    def __init__(self, serial, timeout):
        Subcamada.__init__(self, serial, timeout)
        self.dev = serial  # dev: este atributo mantém uma referência à porta serial
        self.Estados = Enum('Estados', 'ocioso init rx esc') # Estados possíveis da ME
        self.estado = self.Estados.ocioso  # Estado inicial da MEF
        self.buffer = bytearray()  # Buffer que vai armazenar os dados lidos da serial
        self.n_bytes = 0
        self.ser = serial  # objeto serial para conexão com a interface serial do SerialEMU
        self.enable_timeout()

    def envia(self, data):
        final_data = bytearray()
        fcs = crc.CRC16(data)
        msg = fcs.gen_crc()
        #print('Mensagem com CRC: ', msg)
        #dados_digitados = data.encode('utf8')
        #dados_digitados = data.encode('ascii')

        # loop para fazer o enquadramento e substituição dos bytes
        for i in range(0, len(msg)):
            if ((msg[i] == 0x7E) or (msg[i] == 0x7D)):
                trans_byte = bytes([msg[i] ^ 0x20])
                final_data = final_data + b'\x7D' + trans_byte
            else:
                final_data = final_data + bytes([msg[i]])
        final_data = b'\x7E' + final_data + b'\x7E'

        self.ser.write(final_data)  # envia os dados para a serial
        #print('Enviando enquadrado: ', final_data)

    def recebe(self):
        dado_recebido = self.ser.read(1)
        if (dado_recebido == bytearray()):
            return False
        if (self.handle_dado(dado_recebido, self.estado) == True):
            fcs = crc.CRC16('')  # cria o objeto vazio
            fcs.clear()  # limpando
            # adiciona os dados recebidos para checagem do CRC
            fcs.update(self.buffer)
            if (fcs.check_crc() == True):  # se o CRC estiver correto, vai para as etapas abaixo
                #print('Teste buffer com CRC correto: ', self.buffer)
                # remove os dois ultimos bytes, pois são os LSB e MSB do CRC
                self.buffer = self.buffer[0:-2]
                return True
            if (fcs.check_crc() == False):
                print('Deu erro no CRC recebido')
                self.estado = self.Estados.ocioso
                return False

    def handle(self):
        if self.recebe() == True:
            self.upper.recebe(bytes(self.buffer))
            self.buffer.clear() # Limpando o buffer pra não acumular

    def handle_dado(self, byte, estado):
        if (self.estado == self.Estados.ocioso):
            if (byte == b'\x7E'):
                self.n_bytes = 0
                self.estado = self.Estados.init
        if (self.estado == self.Estados.init):
            if (byte == b'\x7E'):
                self.buffer.clear()
                return False
            if (byte == b'\x7D'):
                self.estado = self.Estados.esc
                return False
            else:
                self.buffer += byte
                self.estado = self.Estados.rx
                self.n_bytes += 1
                return False
        if (self.estado == self.Estados.rx):
            if (byte == b'\x7E'):
                self.estado = self.Estados.ocioso
                return True
            if (byte == b'\x7D'):
                self.estado = self.Estados.esc
                return False
            else:
                self.buffer += byte
                self.n_bytes += 1
                return False
        if (self.estado == self.Estados.esc):
            if (byte == b'\x7E'):
                self.buffer = bytearray()
                self.estado = self.Estados.ocioso
            else:
                byte = byte[0] ^ 0x20
                self.buffer += bytes([byte])
                self.estado = self.Estados.rx
            return False

    def handle_timeout(self):
        # Limpa o buffer se ocorrer timeout
        self.buffer.clear()
        #print('Timeout do enquadramento')


from django.urls import reverse
import pytest
from pytest_django.asserts import assertTemplateUsed, assertContains
from lettings.models import Letting, Address

@pytest.mark.django_db
def test_lettings_index_view(client):

    response = client.get(reverse("lettings:index"))
    assertTemplateUsed(response, "lettings/index.html")
    assert response.status_code == 200
    assertContains(response, "Lettings")

@pytest.mark.django_db
def test_lettings_address_view(client):
    title = "Joshua Tree Green Haus /w Hot Tub"
    address = Address.objects.create(
        number=7217,
        street="Bedford Street",
        city="Brunswick",
        state="GA",
        zip_code=31525,
        country_iso_code="USA",
    )
    Letting.objects.create(title=title, address=address)
    res = client.get(reverse("lettings:letting", args=[1]))
    assertTemplateUsed(res, "lettings/letting.html")
    assert res.status_code == 200
    # print (res.content.decode(),3333333333333)
    assertContains(res, "Joshua Tree Green Haus /w Hot Tub")


from string import Template
from pathlib import Path
from typing import Tuple
import logging
import pendulum as pdl
from pendulum.exceptions import ParserError
from freebilly.repository.AbstractWorkLogUnitOfWork import AbstractWorkLogUnitOfWork
from freebilly.repository.AbstractBillUnitOfWork import AbstractBillUnitOfWork
from freebilly.repository.CsvWorkLogRepository import (
    CsvWorkLogRepository,
)  # TODO coupling
from freebilly.services.AbstractServiceLayer import AbstractServiceLayer
from freebilly.domain.AbstractWorkLog import AbstractWorkLog
from freebilly.domain.AbstractWorkSession import AbstractWorkSession
from freebilly.domain.OrderedSetWorkLog import OrderedSetWorkLog  # TODO coupling
from freebilly.domain.PendulumWorkSession import PendulumWorkSession  # TODO coupling

logger = logging.getLogger(__name__)

class ConcreteServiceLayer(AbstractServiceLayer):
    @staticmethod
    def start_session(
        uow: AbstractWorkLogUnitOfWork, client: str, project: str
    ) -> Tuple[
        AbstractWorkLog, AbstractWorkSession
    ]:  # TODO I am saying that but... implementation uses concrete classes...

        logger.info(f"starting session for client {client} and project {project}...")

        work_session = PendulumWorkSession()
        with uow:
            if uow.work_logs.exists(client, project):
                work_log = uow.work_logs.get(client, project)
            else:
                work_log = OrderedSetWorkLog(client, project)

        logger.info(f"session started")

        return work_log, work_session

    @staticmethod
    def end_session(
        uow: AbstractWorkLogUnitOfWork,
        work_log: AbstractWorkLog,
        work_session: AbstractWorkSession,
    ):

        logger.info(f"ending and committing session...")

        with uow:
            work_session.end_session()
            work_log.add_session(work_session)
            uow.commit(work_log)

        logger.info(f"session ended and committed")

    @staticmethod
    def add_session(
        uow: AbstractWorkLogUnitOfWork,
        client: str,
        project: str,
        start_time: str,
        end_time: str,
    ):  # TODO I am saying that but... implementation uses concrete classes...

        logger.info(
            f"pushing supplied session for client {client} and project {project}..."
        )

        try:
            start_time, end_time = pdl.parse(start_time), pdl.parse(end_time)
        except ParserError:
            logger.error(
                "could not parse the start or end time. Is it a valid ISO 8601 format?"
            )
            raise

        work_session = PendulumWorkSession(start_time=start_time, end_time=end_time)

        with uow:
            if uow.work_logs.exists(client, project):
                work_log = uow.work_logs.get(client, project)
            else:
                work_log = OrderedSetWorkLog(client, project)
            work_log.add_session(work_session)
            uow.commit(work_log)

        logger.info(f"session committed")

    @staticmethod
    def produce_bill(
        uow: AbstractBillUnitOfWork,
        template_path: Path,
        work_log_path: Path,
        extra_info: dict,
        client: str,
        project: str,
        start_time: pdl.DateTime,
        end_time: pdl.DateTime,
        hourly_rate: float,
    ) -> None:

        logging.info(
            f"starting bill production for client {client} and project {project}"
        )

        repo = CsvWorkLogRepository(path=work_log_path)
        if not repo.exists(client, project):
            raise ValueError(
                f"could not find any work log data for client {client} and project {project}..."
            )
        work_log = repo.get(client, project)
        quantity = work_log.total_time(start_time, end_time)
        total = round(
            quantity * hourly_rate, 2
        )  # TODO this is business logic leaking into services. This might mean you actually need a Bill domain object, that's a work log and a rate.
        known_keywords = {
            "hourly_rate": hourly_rate,
            "quantity": quantity,
            "total": total,
        }
        if not all(
            [x not in extra_info.keys() for x in ["hourly_rate", "quantity", "total"]]
        ):
            raise ValueError(
                "'hourly_rate', 'quantity', 'total' are special keywords for which data is to be inferred by the program. They "
                "should not appear as keys in the bill info dictionary."
            )
        all_info = extra_info | known_keywords
        with open(str(template_path)) as template_file:
            template_bill = Template(template_file.read())
            filled_bill = template_bill.substitute(all_info)
        # finally, commit to the data.
        with uow:
            uow.commit(filled_bill)

        logging.info("bill produced")


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

from time import mktime
from datetime import datetime

from tomato.discussion.core import db

class Topic(db.Model):

    __tablename__ = 'topic'

    id = db.Column(db.Integer(), primary_key=True)
    members_count = db.Column(db.Integer(), default=0, nullable=False)
    discussions_count = db.Column(db.Integer(), default=0, nullable=False)
    created_at = db.Column(db.DateTime(), default=datetime.utcnow)

    def to_dict(self):
        return dict(
            id=self.id,
            members_count=self.members_count,
            discussions_count=self.discussions_count,
            created_at=mktime(self.created_at.timetuple()),
        )

class TopicMember(db.Model):

    __tablename__ = 'topic_member'
    __table_args__ = (
        db.UniqueConstraint('user_id', 'topic_id', name='ux_topic_member'),
        db.Index('ix_topic_member_admin', 'topic_id', 'is_admin'),
    )

    id = db.Column(db.Integer(), primary_key=True)
    topic_id = db.Column(db.Integer(), nullable=False)
    user_id = db.Column(db.Integer(), nullable=False)
    is_admin = db.Column(db.Boolean(), default=False)
    is_deleted = db.Column(db.Boolean(), default=False)
    updated_at = db.Column(db.DateTime(), default=datetime.utcnow, onupdate=datetime.utcnow)
    created_at = db.Column(db.DateTime(), default=datetime.utcnow)

    def to_dict(self):
        return dict(
            id=self.id,
            topic_id=self.topic_id,
            user_id=self.user_id,
            is_admin=self.is_admin,
            is_deleted=self.is_deleted,
            updated_at=mktime(self.updated_at.timetuple()),
            created_at=mktime(self.created_at.timetuple()),
        )

class Discussion(db.Model):

    __tablename__ = 'topic_discussion'

    id = db.Column(db.Integer(), primary_key=True)
    topic_id = db.Column(db.Integer(), nullable=False)
    user_id = db.Column(db.Integer(), nullable=False)
    title = db.Column(db.String(255), nullable=False)
    content = db.Column(db.Text())
    comments_count = db.Column(db.Integer(), default=0, nullable=False)
    is_deleted = db.Column(db.Boolean(), default=False)
    created_at = db.Column(db.DateTime())

    def to_dict(self):
        return dict(
            id=self.id,
            topic_id=self.topic_id,
            user_id=self.user_id,
            title=self.title,
            content=self.content,
            comments_count=self.comments_count,
            is_deleted=self.is_deleted,
            created_at=mktime(self.created_at.timetuple()),
        )

class Comment(db.Model):

    __tablename__ = 'topic_discussion_comment'

    id = db.Column(db.Integer(), primary_key=True)
    discussion_id = db.Column(db.Integer(), nullable=False)
    user_id = db.Column(db.Integer(), nullable=False)
    content = db.Column(db.Text())
    is_deleted = db.Column(db.Boolean(), default=False)
    created_at = db.Column(db.DateTime())

    def to_dict(self):
        return dict(
            id=self.id,
            discussion_id=self.discussion_id,
            user_id=self.user_id,
            title=self.title,
            content=self.content,
            is_deleted=self.is_deleted,
            created_at=mktime(self.created_at.timetuple()),
        )


import pickle
from selenium import webdriver

driver = webdriver.Chrome()
driver.get('https://kktix.com/events/hellochiayi0522/registrations/new')

# 先卡住輸入帳密之後再按 enter 執行
input("Press any button.")
cookies = driver.get_cookies()
with open('db_cookie.pickle','wb') as f:
    pickle.dump(cookies,f)


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

class SpatialAttention2d(nn.Module):
    '''
    SpatialAttention2d
    2-layer 1x1 conv network with softplus activation.
    <!!!> attention score normalization will be added for experiment.
    '''

    def __init__(self, in_c, act_fn='relu'):
        super(SpatialAttention2d, self).__init__()
        self.conv1 = nn.Conv2d(in_c, 512, 1, 1)  # 1x1 conv
        if act_fn.lower() in ['relu']:
            self.act1 = nn.ReLU()
        elif act_fn.lower() in ['leakyrelu', 'leaky', 'leaky_relu']:
            self.act1 = nn.LeakyReLU()
        self.conv2 = nn.Conv2d(512, 1, 1, 1)  # 1x1 conv
        self.softplus = nn.Softplus(beta=1, threshold=20)  # use default setting.

    def forward(self, x):
        '''
        x : spatial feature map. (b x c x w x h)
        s : softplus attention score
        '''
        x = self.conv1(x)
        x = self.act1(x)
        x = self.conv2(x)
        x = self.softplus(x)
        # print("fsadfsdaf:", x.shape)
        return x

    def __repr__(self):
        return self.__class__.__name__


"""
Fludetector: website, REST API, and data processors for the Fludetector service from UCL.
(c) 2019, UCL <https://www.ucl.ac.uk/

This file is part of Fludetector

Fludetector is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Fludetector is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Fludetector.  If not, see <http://www.gnu.org/licenses/>.
"""
import csv
import os
from functools import wraps
from collections import defaultdict
from datetime import timedelta
from StringIO import StringIO

import sh
from flask import Flask, request, jsonify, render_template, make_response, Response, abort, redirect, url_for, flash
from flask_dotenv import DotEnv

from fludetector import models, scripts, forms
from fludetector.errors import FluDetectorError
from fludetector.models import db, get_region, REGIONS, Model, ModelScore, GoogleScore

app = Flask(__name__)
env = DotEnv(app)

os.environ['GOOGLE_API_KEY'] = app.config['GOOGLE_API_KEY']
os.environ['MODEL_ENGINE'] = app.config['MODEL_ENGINE']
os.environ['TWITTER_ENABLED'] = app.config.get('TWITTER_ENABLED', 'False')

models.init_app(app)
scripts.init_app(app)

@app.errorhandler(FluDetectorError)
def handle_bad_request(e):
    return jsonify({'error': e.message})

def check_auth(username, password):
    """This function is called to check if a username password combination is valid."""
    return username == app.config['ADMIN_USERNAME'] and password == app.config['ADMIN_PASSWORD']

def authenticate():
    """Sends a 401 response that enables basic auth"""
    return Response(
        'Could not verify your access level for that URL.\n'
        'You have to login with proper credentials', 401,
        {'WWW-Authenticate': 'Basic realm="Login Required"'})

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            return authenticate()
        return f(*args, **kwargs)
    return decorated

def calculate_averages(scores):
    averages = defaultdict(lambda: defaultdict(list))
    for (s, smoothed) in scores:
        averages[s.model][s.region].append(smoothed)
    for model, regions in averages.iteritems():
        for region, scores in regions.iteritems():
            yield (model, get_region(region), sum(scores) / len(scores))

def calculate_raw_scores(scores):
    raw_scores = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
    for s, smoothed in scores:
        raw_scores[s.day][s.model][s.region] = smoothed
    return raw_scores

def get_scores_form(public=True):
    all_models = Model.query
    if public:
        all_models = all_models.filter_by(public=public)
    all_models = all_models.all()
    score = None
    for m in all_models:
        score = m.last_score
        if score is not None:
            break
    form = forms.GetScoresWebForm(request.args, model_regions=[(score.model, score.region)])
    return all_models, form

def is_admin_enabled():
    return app.config.get('ADMIN_ENABLED')

@app.route('/')
def home():
    all_models, form = get_scores_form()
    scores = form.scores()

    return render_template(
        'home.html',
        models=all_models,
        form=form,
        regions=REGIONS,
        averages=calculate_averages(scores),
        raw_scores=calculate_raw_scores(scores),
        admin_enabled=is_admin_enabled()
    )

@app.route('/export/')
def export():
    all_models, form = get_scores_form()

    raw_scores = calculate_raw_scores(form.scores())

    fd = StringIO()
    csv_fd = csv.writer(fd)

    headers = [
        [''] + [m.name for m, r in form.model_regions.data],
        ['Date'] + [REGIONS[r] for m, r in form.model_regions.data]
    ]
    for r in headers:
        csv_fd.writerow(r)

    for day, models_ in sorted(raw_scores.iteritems()):
        row = [[day.strftime('%Y-%m-%d')]]
        row.append([models_[m][r] for m, r in form.model_regions.data])
        csv_fd.writerow(row)

    resp = make_response(fd.getvalue())
    resp.headers["Content-Disposition"] = "attachment; filename=fluedetector-export.csv"
    resp.headers["Content-type"] = "text/csv"
    return resp

@app.route('/about/')
def about():
    return render_template('about.html')

@app.route('/docs/')
def docs():
    model = Model.query.filter_by(public=True).first()
    score = model.scores.first()
    example = {
        'model': model.id,
        'region': score.region,
        'resolution': 'day',
        'smoothing': 3,
        'start': score.day - timedelta(days=7),
        'end': score.day + timedelta(days=7)
    }
    return render_template('docs.html', example=example, model=model)

@app.route('/api/scores/')
def api_scores():
    form = forms.GetScoresApiForm(request.args)

    if not form.validate():
        return jsonify({'errors': form.errors}), 400

    model = form.model.data

    return jsonify({
        'model': {'id': model.id, 'name': model.name},
        'region': form.region.data,
        'start': form.start.data.strftime('%Y-%m-%d'),
        'end': form.end.data.strftime('%Y-%m-%d'),
        'resolution': form.resolution.data,
        'smoothing': form.smoothing.data,
        'scores': form.scores()})

@app.route('/api/models/')
def api_models():
    models = Model.query.filter_by(public=True).all()
    return jsonify([
        {'id': m.id, 'name': m.name}
        for m in models])

@app.route('/admin/')
@requires_auth
def admin_home():
    all_models, form = get_scores_form(public=False)
    scores = form.scores()

    return render_template(
        'admin/home.html',
        models=all_models,
        form=form,
        regions=REGIONS,
        all_model_regions=[(m, r) for m in all_models for r in m.regions],
        averages=calculate_averages(scores),
        raw_scores=calculate_raw_scores(scores))

@app.route('/admin/models/')
@requires_auth
def admin_list_models():
    return render_template(
        'admin/model_list.html',
        models=Model.query.all(),
        region_codes=list(REGIONS.keys()))

@app.route('/admin/models/<int:id>/')
@requires_auth
def admin_retrieve_model(id):
    model = Model.query.get(id)
    if not model:
        abort(404)
    return render_template('admin/model_retrieve.html', model=model, REGIONS=REGIONS)

@app.route('/admin/models/<int:id>/delete/', methods=['GET', 'POST'])
@requires_auth
def admin_delete_model(id):
    model = Model.query.get(id)
    if not model:
        abort(404)
    form = forms.DeleteModelForm(model)
    if form.validate_on_submit():
        for term in model.google_terms:
            # Delete any orphaned GoogleTerms
            # There should be a way to cascade these deletes automatically?
            # I couldn't work it out :(
            if len(term.models) == 1:
                db.session.delete(term)
        db.session.delete(model)
        db.session.commit()
        flash('Model deleted!', 'success')
        return redirect(url_for('admin_list_models'))
    return render_template('admin/model_delete.html', form=form, model=model)

@app.route('/admin/models/create/', methods=['GET', 'POST'])
@requires_auth
def admin_create_model():
    form = forms.CreateModelForm.factory(request.args.get('type', 'csv'))
    if form.validate_on_submit():
        model = Model()
        form.populate_obj(model)
        db.session.add(model)
        db.session.commit()
        return redirect(url_for('admin_retrieve_model', id=model.id))
    return render_template('admin/model_create.html', form=form)

@app.route('/admin/models/<int:id>/update/', methods=['GET', 'POST'])
@requires_auth
def admin_update_model(id):
    model = Model.query.get(id)
    if not model:
        abort(404)
    form = forms.CreateModelForm.factory(model.type, model=model)
    if form.validate_on_submit():
        form.populate_obj(model)
        db.session.add(model)
        db.session.commit()
        return redirect(url_for('admin_retrieve_model', id=model.id))
    return render_template('admin/model_create.html', form=form, model=model)

@app.route('/admin/models/<int:id>/run/', methods=['GET', 'POST'])
@requires_auth
def admin_run_model(id):
    model = Model.query.get(id)
    if not model:
        abort(404)
    form = forms.RunModelForm.factory(model)
    if form.validate_on_submit():
        run = sh.Command('./scripts/run.sh')
        run('runmodel', str(id), _bg=True, **form.to_dict())
        flash('Model running in background, check logs for details', 'info')
        return redirect(url_for('admin_list_models'))
    return render_template('admin/model_run.html', form=form, model=model)

@app.route('/admin/logs/')
@requires_auth
def admin_list_logs():
    logs = os.listdir('logs')
    return render_template('admin/log_list.html', logs=logs)

@app.route('/admin/logs/<filename>')
@requires_auth
def admin_retrieve_log(filename):
    """This feels dangerous. However, filename cannot contain slashes so we
    should be confined to direct children of the logs directory. Also, only
    admins can access this file so hopefully exposure is minimal.

    Famous last words :)
    """
    with open(os.path.join('logs', filename), 'rb') as fd:
        return fd.read(), {'Content-Type': 'text/plain'}

def admin_export_google(model, start, end, writer):
    writer.writerow(['Term', 'Day', 'Value'])
    for term in model.google_terms:
        scores = term.scores.filter(GoogleScore.day >= start, GoogleScore.day < end)
        for score in scores:
            writer.writerow([term.term, score.day.strftime('%Y-%m-%d'), score.value])

def admin_export_csv(model, start, end, writer):
    scores = model.scores.filter(ModelScore.day >= start, ModelScore.day < end)
    by_date = defaultdict(lambda: defaultdict(int))
    regions = set()
    for s in scores:
        regions.add(s.region)
        by_date[s.day][s.region] = s.value
    regions = list(regions)
    writer.writerow(['Date'] + [REGIONS[code] for code in regions])
    for day, by_region in sorted(by_date.iteritems()):
        writer.writerow([day.strftime('%Y-%m-%d')] + [by_region.get(code) for code in regions])

@app.route('/admin/models/<int:id>/export/', methods=['GET', 'POST'])
@requires_auth
def admin_export_model(id):
    model = Model.query.get(id)
    if not model:
        abort(404)
    form = forms.ExportModelForm()

    if form.validate_on_submit():
        fd = StringIO()
        csv_fd = csv.writer(fd)

        {
            'google': admin_export_google,
            'csv': admin_export_csv
        }.get(model.type)(model, form.start.data, form.end.data, csv_fd)

        resp = make_response(fd.getvalue())
        resp.headers["Content-Disposition"] = "attachment; filename=fluedetector-export.csv"
        resp.headers["Content-type"] = "text/csv"
        return resp

    return render_template('admin/model_export.html', form=form, model=model)


"""

=======================================================================
WARNING: PYSHEETDATA IS NOT SECURE AGAINST MALICIOUSLY CONSTRUCTED DATA
=======================================================================
it is intended to be used only with trusted data.

note on file names:
    file names including ' : [ or ] are likely to mess things up, as
    they tend to do to other spreadsheet programs as well (due to
    the way file names and references are annotated in the xml file)

given a ods file -
    extract
    parse contents file
        for each instance of cell name:
            add cell to list
                get contents_string
                from string:
                    get value
                    get raw_formula
                    get formula from raw_formula
                        (use brackets to find reference)  - (so much easier)
                    get xy ref
                    get a1 ref
    return library or file

scripting variables:
    cells[spreadsheet reference] - gets a cell object, or matrix of objects
    sheet.rows[y]
    sheet.columns
"""

import zipfile
import lxml.etree as etree
import os.path as path

import eval.storage as storage
import eval.parser as parser
import script

class SheetDataError(Exception):
    pass

class LibComponent:
    """
    abstract library component class, sub-classed by cell, sheet, book, lib
    """
    def __init__(self):
        self.parent = None
        self._settings = {}
        self._sheet = None
        self._book = None
        self._lib = None

    def __str__(self):
        return '%s %s' % (self.class_name, self.identifier)

    @property
    def identifier(self):
        raise NotImplementedError('LibComponent should only be inherited from')

    @property
    def class_name(self):
        return self.__class__.__name__

    @property
    def inc_set(self):
        return self._settings.get('inc_set', True)

    @property
    def included(self):
        if all([parent.inc_set for parent in self.parents]):
            return True

    @property
    def parents(self):
        # returns list of parent row, column, sheet, book, lib
        return [getattr(self, parent_s) for parent_s in
                ['row', 'column', 'sheet', 'book', 'lib']
                if getattr(self, parent_s) is not None]

    #######################
    # parent object getters
    # iterates through hierarchy in order and attempts to find
    # appropriate instance

    @property
    def row(self):
        try:
            return self._row  # only cells have this attr
        except AttributeError:
            raise NotImplementedError('%s does not have a row parent obj'
                                      % self)

    @property
    def column(self):
        try:
            return self._column  # see above
        except AttributeError:
            raise NotImplementedError('%s does not have a column parent obj'
                                      % self)

    @property
    def sheet(self):
        return self.get_parent('Sheet')

    @property
    def book(self):
        return self.get_parent('Book')

    @property
    def lib(self):
        return self.get_parent('Library')

    @property
    def library(self):
        # backwards compatibility, hurrah.
        return self.get_parent('Library')

    @property
    def file(self):
        return self.get_parent('Book').file

    @file.setter
    def file(self, file):
        self.get_parent('Book').file = file

    def get_parent(self, hierarchy_s=None):
        # return parent in hierarchy of name 'hierarchy_s'
        # if none, returns direct parent of inst.
        if self.class_name == hierarchy_s:
            return self
        parent_attr = getattr(self, '_parent', None)
        if hierarchy_s is None:
            hierarchy_s = 'parent'
        if parent_attr is None:
            raise NotImplementedError(self.class_name + ' does not have a ' +
                                      hierarchy_s)
        if hierarchy_s is None:
            return self.parent
        else:
            return parent_attr.get_parent(hierarchy_s)

    def ns(self, string):
        # returns namespace instance for lib comp
        return self.file.map.ns(string)

class Cell(LibComponent):
    # in some sheets there are going to be a LOT of these, should be
    # coded as such.
    # 78,000 plus in some sheets that I'll be using this for,
    # and I'm sure others will have far more
    def __init__(self, cell_data, position, sheet, mode='standard'):
        super().__init__()
        self.mode = mode
        # changing mode allows different amounts of data to be stored
        # for each cell. speed vs memory usage
        self._cell_element = cell_data
        self._position = position
        self._cached_position = position
        self._parent = sheet
        # stores new values, without applying them to _cell_element
        self._new_attrib = {}
        self._new_text = None
        self._new_script = None
        self._change_flag = False  # not yet implemented
        # output vars

        # may be added in the future to speed up program
        # self._dependencies
        # self._dependants

    @property
    def identifier(self):
        return self.a1

    @property
    def has_contents(self):
        if self._cell_element is not None:  # for etree reasons, cannot
                # just use self._cell_element
            return True
        else:
            return False

    @property
    def data_type(self):
        return self.get('office:value-type')

    @data_type.setter
    def data_type(self, value):
        self.set('office:value-type', value)

    @property
    def content(self):
        # will be refactored as 'value' to return either string
        # or float value
        if self.data_type == 'string':
            self.evaluate()
            return self.text
        else:
            return self.value

    @content.setter
    def content(self, content):
        try:
            self.value = float(content)
        except ValueError:
            self.text = content

    @property
    def value(self):
        # at the moment, reevaluates all cells in dependency tree
        # in the future, once dependants and change flags are
        # instituted, will only reevaluate if a cell in the tree has
        # changed
        self.evaluate()
        return float(self.get('office:value'))

    @value.setter
    def value(self, value):
        # note: this only returns a numerical value only
        # this will be refactored soon to return either str or float
        self.set('office:value', float(value))
        self.data_type = 'float'

    @property
    def cached_value(self):
        # always returns original value from the xml file
        return float(self.get('office:value', True))

    @property
    def text(self):
        # text getter/setter is different from other attributes because
        # it is stored separately in the xml file
        if self._new_text is not None:
            return self._new_text
        return self._cell_element[0].text

    @text.setter
    def text(self, string):
        # not using self.set because text is stored separately
        # (ask whoever started that standard, no idea why)
        self.data_type = 'string'
        if string != self.text:
            self._new_text = string

    @property
    def cached_text(self):
        return self._cell_element[0].text  # get text directly from ET

    @property
    # this is the formula as modified to appear as it does as typed
    # by a user in the spreadsheet program (...eventually)
    def formula(self):
        return self.get('table:formula')[3:]

    @property
    # this is the formula as stored in the xml file, with formatting
    # left in
    def raw_formula(self):
        return self.get('table:formula')

    @property
    def is_script(self):
        if self.text.startswith(PYSCRIPT_FLAG):
            return True
        else:
            return False

    @property
    def script(self):
        # if a new script has been set, use that
        # otherwise, use the cached text to see if self is script.
        # scripts themselves can change self text value.
        if self._new_script:
            return self._new_script
        if self.is_script:
            return self.text[len(PYSCRIPT_FLAG):]

    @script.setter
    def script(self, script_string):
        self._new_script = script_string

    @property
    def a1(self):
        return a1_from_xy(self.position)

    @a1.setter
    def a1(self, a1_string):
        self.position = xy_from_a1(a1_string)

    @property
    def position(self):
        return self._position

    @position.setter
    def position(self, tuple_or_list):
        self.position = (tuple_or_list[0], tuple_or_list[1])

    @property
    def cached_position(self):
        return self._cached_position

    @property
    def column(self):
        return self.sheet.columns[self.position[0]]

    @property
    def row(self):
        return self.sheet[self.position[1]]

    @property
    # returns dictionary of dependencies of self.
    # referencing string is key
    # may just have them be stored as standard
    # I -don't think- this needs to be sent over to _new_attrib, but
    # if that proves to be the case, this will need to be updated.
    def dependencies(self):
        return self.find_dependencies()

    #############################
    # Output props
    @property
    def included(self):
        # if all parent objects are set to be included in output,
        # returns true as cell is to be included in output file
        if all([self.book.inc_set,
                self.sheet.inc_set,
                self.row.inc_set,
                self.column.inc_set]):
            return True

    def get(self, string, cached=False):
        # get method called by property getters that use the attrib
        # dictionary
        # check if attribute has a new value in self._new_attrib
        # before getting it from the element tree attrib dictionary
        string = self.ns(string)
        if string in self._new_attrib and not cached:
            return self._new_attrib[string]
        else:
            return self._cell_element.get(string)

    def set(self, key, entry):
        # set method called by properties using the attrib dict
        # if new val is different from original, put in new_attrib
        # dict, rather than editing loaded element tree. This allows
        # changes to be reverted
        if entry != self.get(key):
            ns_key = self.ns(key)
            self._new_attrib[ns_key] = entry

    def find_dependencies(self):
        # returns dictionary of referenced cells with reference as key
        # if reference is a range (like 'A1:B2') returns matrix
        if not self.has_contents or (self.raw_formula is None and
                                     self.script is None):
            return {}
        else:
            dependencies = {}
            # look in formula first
            if self.raw_formula is not None:
                # find start of a reference
                is_quote = False
                start = None
                for x in range(0, len(self.raw_formula)):
                    if self.raw_formula[x] == "'":
                        is_quote = not is_quote
                    elif self.raw_formula[x] == '[' and not is_quote:
                        # reference found!
                        start = x
                    elif self.raw_formula[x] == ']' and not is_quote:
                        # set reference string
                        reference = self.raw_formula[start + 1: x]
                        # set default values, will be changed in a
                        # moment if needed
                        dependencies['[' + reference + ']'] = \
                            self.cell_from_reference(reference)
            if self.script is not None:
                start = None
                script_s = self.script
                for x in range(0, len(script_s)):
                    if script_s[x: x + 6] == 'cells[':
                        start = x
                    elif script_s[x] == ']' and \
                            start is not None:
                        reference = script_s[start + 7:x - 1]
                        start = None
                        dependencies['cells[' + reference + ']'] = \
                            self.cell_from_reference(reference)
            return dependencies

    def cell_from_reference(self, reference):
        reference_type = 'cell'  # default reference
        range_start = None
        range_end = None
        # now that there's a reference, iterate through it
        # to see if there's a ':' outside of quotes
        # denoting a range of cells
        if ':' in reference:
            index = find_unquoted(':', reference)
            if index is not None:
                reference_type = 'range'
                range_start = reference[:index - 1]
                range_end = reference[index + 1:]
        if reference_type == 'cell':
            reference_parts = break_apart_reference(reference)
            reference_parts = self.complete_reference_parts(*reference_parts)
            return self.library[reference_parts]
        elif reference_type == 'range':
            start_parts = break_apart_reference(range_start)
            start_parts = self.complete_reference_parts(
                    start_parts)
            end_parts = break_apart_reference(range_end)
            end_parts = self.complete_reference_parts(
                    end_parts)
            return CellRange(
                self.sheet.book.library[start_parts],
                self.sheet.book.library[end_parts]
            )

    def complete_reference_parts(self, *parts):
        # unfilled parts of reference are completed with
        # self sheet and book
        sheet_default = self.sheet.name
        book_default = self.sheet.book.file_name
        length = len(parts)
        if 0 < length <= 3:  # if length is within possible ref. depths
            additions = [book_default, sheet_default]
            # add parts of reference that are missing from reference
            return additions[0: 3 - length] + list(parts)
        else:
            raise SheetDataError('len of reference parts outside 1-3 range')

    def evaluate(self, recursive=True, scripts=True):
        if self.has_contents:
            if recursive:
                for cell in self.dependencies:
                    self.dependencies[cell].evaluate()
            if scripts and self.script:
                self.run_script()
            elif self.raw_formula:
                evaluation = parser.evaluate(self.formula,
                                             self.dependencies)
                # if returned is value, put it in self.value
                # if that doesn't work, put it in self.string
                try:
                    self.value = float(evaluation)
                except ValueError:
                    try:
                        self.text = str(evaluation)
                    except ValueError:
                        print('could not set new value')
                        pass

    def run_script(self, auto_import=True):
        # dependencies is list of cells that are used

        class CellReferencer:
            def __init__(self, lookup_formula):
                self.lookup = lookup_formula

            def __getitem__(self, reference_string):
                return self.lookup(reference_string)

        if self.has_contents and self.is_script:
            # set script vars that can be called by the script
            script.cell = self
            script.cells = CellReferencer(self.cell_from_reference)
            script_string = self.script
            if auto_import:
                script_string = 'from script import *\n' + self.script
            exec(script_string)
        else:
            return ''

class CellRange:
    # range of cells intended for evaluator - acts similar to cell
    # in that it returns (total) value, dictionary of all dependencies,
    # etc
    def __init__(self, start_cell, end_cell):
        sheet_list = start_cell.sheet.book.sheet_list

        # create 3d matrix of cells inside the range (sheet, row, cell)
        indexes = {
            'sheets': [sheet_list.index(start_cell.sheet.name),
                       sheet_list.index(end_cell.sheet.name)],
            'rows': [start_cell.position[1], end_cell.position[1]],
            'cells': [start_cell.position[0], end_cell.position[0]]
        }
        for pair in indexes:
            indexes[pair].sort()
        self._matrix = []
        for sheet in start_cell.sheet.book.sheet_list[indexes['sheets'][0]:
                                                      indexes['sheets'][1]]:
            sheet_matrix = []
            self._matrix.append(sheet_matrix)
            for row in sheet.matrix[indexes['rows'][0]:indexes['rows'][1]]:
                row_matrix = []
                sheet_matrix.append(row_matrix)
                for cell in row[indexes['cells'][0]:indexes['cells'][1]]:
                    row_matrix.append(cell)

    @property
    def identifier(self):
        return '%s %s' % (self.start_cell.a1, self.end_cell.a1)

    @property
    def start_cell(self):
        return self.matrix[1][1][1]

    @property
    def end_cell(self):
        return self.matrix[-1][-1][-1]

    @property
    def matrix(self):
        return self._matrix

    @matrix.setter
    def matrix(self, matrix):
        self._matrix = matrix

    @property
    def cells(self):
        cells = []
        [[[cells.append(cell) for cell in row] for row in sheet] for sheet in
         self.matrix]
        return cells

    @property
    def value(self):
        return sum([sum([sum([cell.value for cell in row])
                   for row in sheet]) for sheet in self.matrix])

    @property
    def dependencies(self):
        # returns dictionary of dependencies
        # reference string is key, referenced cell or range is value
        d = {}
        for sheet in self.matrix:
            for row in sheet:
                for cell in row:
                    d.update(cell.dependencies)
        return d

    def evaluate(self):  # evaluates each cell in matrix
        [[[cell.evaluate() for cell in row]
          for row in sheet]
         for sheet in self.matrix]

class Row(LibComponent):
    def __init__(self, sheet, y, tree):
        super().__init__()
        self.y = y
        self._parent = sheet
        self._tree = tree
        self._cells = []
        self._loaded = False
        self._settings = {}

    def __getitem__(self, item):
        if not self._loaded:
            self.load()
        return self._cells[item]

    @property
    def identifier(self):
        return self.y

    @property
    def list(self):
        return [cell.content for cell in self._cells]

    def load(self):
        cell_elements = self._tree.findall(self.ns('table:table-cell'))
        [self._cells.append(Cell(cell_elements[x], (x, self.y), self.sheet))
         for x in range(0, len(cell_elements))]
        self._loaded = True

class Column(LibComponent):
    # used by spreadsheet xml for storing formatting, also useful for
    # references.
    def __init__(self, sheet, x, tree):
        super().__init__()
        self.x = x
        self.parent = sheet
        self._tree = tree

    def __getitem__(self, y):
        # unlike row, does not store cells.
        return self.sheet[(self.x, y)]

    @property
    def identifier(self):
        return x_to_letter(self.x)

class Sheet(LibComponent):
    def __init__(self, book, element_tree):
        super().__init__()
        self._rows = []
        self._columns = []
        self._tree = element_tree
        self._attributes = self._tree.attrib
        self._loaded = False
        self._parent = book
        self.name = [self._attributes[attribute]
                     for attribute in self._attributes
                     if not attribute.endswith('style-name') and
                     attribute.endswith('name')][0]
        self._settings = {}

    def __getitem__(self, item):
        # if item is tuple or list, return the referenced cell
        # if item is string, convert it from a1 and return the cell
        # if item is int, return the referenced row
        # if self is not yet loaded, do that now
        if not self._loaded:
            self.load()
        if isinstance(item, (list, tuple)):
            x, y = item
        elif isinstance(item, str):
            x, y = xy_from_a1(item)
        else:
            try:
                y = item
            except:
                raise KeyError(str(item) + ' is not a valid key for sheet ' +
                               self.name)
            else:
                return self._rows[y]
        return self._rows[y][x]

    @property
    def identifier(self):
        return self.name

    @property
    def columns(self):
        return self._columns

    @property
    def matrix(self):
        """
        returns a matrix of sheet data
        :return: matrix
        """
        return [row.list for row in self._rows]

    def load(self):
        # loads rows and columns into sheet lists

        # load rows
        row_elements = self._tree.findall(self.ns('table:table-row'))
        [self._rows.append(Row(self, y, row_elements[y])) for y in
         range(0, len(row_elements))]
        self._loaded = True

        # load columns
        column_elements = self._tree.findall(self.ns('table:table-column'))
        x = 0
        for column_tree in column_elements:
            repeat = column_tree.get(self.ns(
                    'table:number-columns-repeated'))
            if repeat:
                for x in range(0, int(repeat)):
                    self._columns.append(Column(self, x, column_tree))
                    x += 1
            else:
                self._columns.append(Column(self, x, column_tree))
                x += 1

    def find(self, column_or_row, name_to_find,
             name_index=0, case_sensitive=False):
        if column_or_row == 'row' or column_or_row == 'rows':
            list_to_search = self._rows
        elif column_or_row == 'column' or column_or_row == 'columns':
            list_to_search = self._columns
        else:
            raise KeyError('list to search should be \'row\' or \'column\', '
                           'not %s' % column_or_row)
        if not case_sensitive:
            name_to_find = name_to_find.lower()
        result = None
        for row in list_to_search:
            row_name = row[name_index].text
            if not case_sensitive:
                row_name = row_name.lower()
            if row_name == name_to_find:
                if result is None:
                    result = row
                else:
                    raise KeyError('multiple rows with name \'%s\'' %
                                   name_to_find)

class Book(LibComponent):
    # gets passed relevant dictionary of elements by file.load()
    # should not load cells until that function is called
    # then pass on relevant sub-dictionary to sheets that are created
    # so on
    def __init__(self, library, file, element_tree):
        super().__init__()
        self._file = file
        self._element_tree = element_tree
        self._parent = library
        self._settings = {}
        self.sheets = {}
        self.sheet_list = []  # list of sheets, in order of file

    @property
    def identifier(self):
        return path.split(self._file.file_id)[-1]

    @property
    def file_name(self):
        return self.file.file_id

    @property
    def file(self):
        return self._file

    @file.setter
    def file(self, file):
        self._file = file

    @property
    def dictionary(self):
        """
        creates and returns dictionary of book content
        :return: content dictionary
        """
        return {sheet.name: sheet.matrix for sheet in self.sheets}

    def __getitem__(self, item):
        item = strip_quotes(item.lower())
        if item in self.sheets:
            pass
        elif item in self.sheet_list:
            if self.library.recursive:
                self.load(item)
            else:
                raise KeyError(str(item) + ' is in Book ' + self.file_name +
                               ' but not loaded, and recursive loading is'
                               ' not enabled')
        else:
            raise KeyError(str(item) + ' is not in Book ' + self.file_name)
        return self.sheets[item]

    def load(self, sheet_to_load):
        # load sheet of name (arg) or else load everything
        # from xml module, response to simple 'if self._element_tree';
        # "FutureWarning: The behavior of this method will change in
        # future versions. Use specific 'len(elem)' or
        # 'elem is not None' test instead."
        if self._element_tree is not None:
            [self.add_sheet(Sheet(self, sheet))
             for sheet in self._element_tree.findall(self.ns('table:table'))
             if (sheet.attrib[self.ns('table:name')] == sheet_to_load or
                 sheet_to_load is None)]
        # check

    def write(self, name, overwrite):
        """
        writes content dictionary to a file of name
        :param name: name to write as.
        :param overwrite: Bool, write over existing file of name
        """

    def add_sheet(self, sheet):
        self.sheets[sheet.name.lower()] = sheet

class Library(LibComponent):
    def __init__(self, list_of_addresses, recursive_loading=False):
        super().__init__()
        self.list = list_of_addresses
        self.files = {address: File(address, self) for address in self.list}
        self.books = {}
        self.recursive = recursive_loading
        self._settings = {}

        [self.files[file].load() for file in self.files]

    @property
    def identifier(self):
        return 'base'

    def load_books(self):
        for book_name in self.list:
            self.load_book(book_name)

    def load_book(self, book_name):
        self.files[book_name] = File(book_name, self)
        self.files[book_name].load()

    def __getitem__(self, item):
        # the new improved method for returning cells / books / sheets
        # if item is list, break apart and recall self
        # (allows passing of lists of getters)
        if isinstance(item, (list, tuple)):
            return self[item[0]][item[1]][item[2]]
        item = remove_file_prefix(strip_quotes(item))
        if item in self.books:
            book = self.books[item]
        elif '/' not in item and \
             any([book_name.endswith(item) for book_name in self.books]):
            # if there are no backspaces in item string, and one of the
            # library's books ends with the string 'item,'
            # that's the book
            book = [self.books[book_name] for book_name in self.books if
                    book_name.endswith(item)][0]
            # this has the potential for bugs, should be refined in the
            # future
        elif self.recursive:
            try:
                self.load_book(item)
                book = self.books[item]
            except:
                error_string = 'could not find referenced book ' + str(
                       item)
                raise SheetDataError(error_string)
        else:
            error_string = 'cell referenced cell not in loaded'\
                                         'book' + str(item)
            raise SheetDataError(error_string)
        return book

class File:
    def __init__(self, file_address, library=None):
        self.file_id = file_address
        self.body = None
        self.map = None
        self.library = library

        if self.file_id[:7] == 'file://':
            self.file_id = self.file_id[7:]

        if library is None:
            self.library = Library([file_address])

    def load(self, sheet_name=None):
        # load cell instances into
        #
        # dictionary (library)
        # of dictionaries (book)
        # of lists (sheet)
        # of lists (row)
        # of objects (cell)

        # tree structure:
        # content
        #   body
        #       spreadsheet
        #           table
        #               table-row
        #                   table-cell

        # create element tree by extracting xml file and parsing it
        with zipfile.ZipFile(self.file_id).open('content.xml') as content:
            data = etree.parse(content).getroot()

            # get prefix map
            self.map = NSMap(data.nsmap)

            # set library.books [file_address]
            # to book of appropriate data
            if self.file_id not in self.library.books:
                body_element = data.find(self.ns('office:body'))
                spreadsheet_element = body_element.find(self.ns(
                        'office:spreadsheet'))
                self.library.books[self.file_id] = \
                    Book(self.library, self, spreadsheet_element)

            self.library.books[self.file_id].load(sheet_name)

    def ns(self, s):
        return self.map.ns(s)

class NSMap:
    # simple object for dealing with namespace mapping
    def __init__(self, map_dictionary):
        self.dict = map_dictionary

    def ns(self, s):
        # return string with prefix converted to namespace
        # string:tag --> {namespace}tag
        for x in range(0, len(s) - 1):
            if s[x] == ':':
                prefix = s[:x]
                suffix = s[x + 1:]
                s = '{%s}%s' % (self.dict[prefix], suffix)
                break
        return s

def a1_from_xy(pos):
    # returns excel-type cell given X, Y position
    # A corresponds to 0 in x-axis
    # 1 corresponds to 0 in y-axis

    x_ref = x_to_letter(pos[0] + 1)
    y_ref = str(pos[1] + 1)

    return x_ref + y_ref

def xy_from_a1(s):
    # returns x, y from inputted 'a1' string
    s = s.lower()
    x = None
    y = None
    for a in range(0, len(s)):
        if s[a - 1] in storage.ENGLISH_ALPHABET and \
                s[a] in storage.ARABIC_NUMBERS:
            x = s[:a]
            y = s[a:]
            break

    if x is None:
        raise SheetDataError('could not parse reference to %s' % s)

    x = letter_to_x(x)
    y = int(y) - 1

    return x, y

def x_to_letter(x):
    # takes in a number and returns it as letter(s)
    # ex.: 1 = 'a', 26 = 'aa'

    l = []
    s = ''

    # while x > 26 divides it by 26 and adds the remainder to l
    while x > 26:
        l.append(x % 26)
        x //= 26

    l.append(x % 26)

    # since the above worked from the right
    # (smallest to largest place digit)
    # this reverses the order
    l = l[::-1]

    # convert each i in l into the corresponding letter
    for i in l:
        s += storage.ENGLISH_ALPHABET[i - 1]

    return s

def letter_to_x(s):
    # converts letter or letter series to arabic number value
    # example: a --> 0, ab --> 28
    s = s.lower()[::-1]
    val = -1  # starts at -1 so that a, when evaluated as 1, returns 0
    for x in range(0, len(s)):
        val += (ord(s[x]) - 96) * 26 ** x
    return val

def break_apart_reference(s):
    # takes a reference string and breaks it down into a list of
    # file name, sheet name, cell name
    parts = []

    # iterate backwards through string looking for '.' then '#$'
    dot = find_unquoted('.', s, True)
    if dot is None:
        parts.append(s)
        return parts
    elif dot is 0:
        parts.append(s[dot + 1:])
        return parts
    else:
        parts.append(s[dot + 1:])
    # now try to find the #$
    hd = find_unquoted('#$', s, True)
    if hd is None:
        parts.append(s[:dot])
    else:
        parts.append(s[hd + 2: dot])
        parts.append(s[:hd])
    # reverse order to go book, sheet, cell rather than the reverse
    return list(reversed(parts))

def find_unquoted(target, string, back=False, list_mode=False):
    # finds unquoted target in string and returns the position of the
    # first character
    # this function is used for finding a string in a parent string
    # but only if it is not enclosed in quotes - useful for finding a
    # cell reference in a formula, where you want to find 'cell['
    # but not when used as in a script saying
    # print('you can access cells by typing cell[<reference>]')
    motion = 1
    index = 0
    target_length = len(target)
    if back:
        motion *= -1
        index = len(string) - 1
    if list_mode:
        matches = []
        while 0 <= index < len(string):
            if string[index] == "'":
                index += motion
                while string[index] != "'":
                    index += motion
            elif string[index: index + target_length] == target:
                matches.append(index)
            index += motion
        return matches
    else:
        while string[index: index + target_length] != target:
            if string[index] == "'":
                index += motion
                while string[index] != "'":
                    index += motion
            index += motion
            if not 0 <= index < len(string):
                return
        return index

PYSCRIPT_FLAG = 'py='  # flag denoting start of python script

def strip_quotes(string):
    def is_quote(char):
            if char == '\'' or char == '\"':
                return True
            else:
                return False

    if is_quote(string[0]) and is_quote(string[-1]):
        string = string[1:-1]
    return string

def remove_file_prefix(string):
    pre = 'file://'
    if string.startswith(pre):
        string = string[len(pre):]
    return string


from __future__ import print_function

import argparse
import json
import sys

import jref.version
import jref.reference

from jref.context import CLIContext, LocalContext, RemoteContext

def parse_args():
    cli = argparse.ArgumentParser()
    cli.set_defaults(context_class=CLIContext)

    cli.add_argument(
        '--version', action='version',
        version='%(prog)s v{}'.format(jref.version.version))

    cli.add_argument(
        '--max-recursion', type=int, help='''Maximum recursion depth for
        resolving references (default: {})'''
        .format(jref.reference.MAXIMUM_REFERENCE_RECURSION_DEPTH))
    cli.add_argument(
        '--local-root', default='', help='''Root path for references to the
        local filesystem. Note: remote resources are not allowed to reference
        local paths.''')

    uri_handling = cli.add_mutually_exclusive_group()
    uri_handling.add_argument(
        '--local-only', dest='context_class', action='store_const',
        const=LocalContext, help='''Accept only references to files in the local
        filesystem''')
    uri_handling.add_argument(
        '--remote-only', dest='context_class', action='store_const',
        const=RemoteContext, help='''Accept only references using remote
        URIs''')

    cli.add_argument('reference', nargs='*', help='A JSON Reference to evaluate')

    return cli.parse_args()

def print_reference(ref):
    print(json.dumps(
        ref.expand(), indent=2, separators=(',', ': '), sort_keys=True))

def main():
    config = parse_args()

    if config.max_recursion:
        jref.reference.MAXIMUM_REFERENCE_RECURSION_DEPTH = \
            config.max_recursion

    ctx_args = {}
    if config.local_root:
        ctx_args['root'] = config.local_root
    ctx = config.context_class(**ctx_args)

    for user_ref in config.reference:
        ref = ctx.parse_reference(user_ref)
        print_reference(ref)

if __name__ == '__main__':
    # Don't show __main__.py as executable in usage string
    sys.argv[0] = 'jref'

    main()


# -*- coding: utf-8 -*-
"""
Created on 2019/9/25 10:17
@Author:lilu
@Desc:
"""
import os
import random
import string
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import shutil

# 字体的位置
font_path = './verification_code/fonts/arial.ttf'
# 生成几位数的验证码
number = 4
# 生成验证码图片的高度和宽度
size = (100, 30)
# 背景颜色，默认为白色
bgcolor = (255, 255, 255)
# 字体颜色，默认为蓝色
fontcolor = (0, 0, 255)
# 干扰线颜色，默认为红色
linecolor = (255, 0, 0)
# 是否要加入干扰线
draw_line = True
# 加入干扰线条数的上下限
line_number = (1, 5)

def gen_text():
    """随机生成一个字符串"""
    source = list(string.ascii_letters)
    for index in range(0, 10):
        source.append(str(index))
    return ''.join(random.sample(source, number))  # number是生成验证码的位数

def gen_line(draw, width, height):
    """绘制干扰线"""
    begin = (random.randint(0, width), random.randint(0, height))
    end = (random.randint(0, width), random.randint(0, height))
    draw.line([begin, end], fill=linecolor)

def gen_code(num):
    """生成验证码"""

    while num > 0:
        width, height = size  # 宽和高
        image = Image.new('RGBA', (width, height), bgcolor)  # 创建图片
        font = ImageFont.truetype(font_path, 25)  # 验证码的字体
        draw = ImageDraw.Draw(image)  # 创建画笔
        text = gen_text()  # 生成字符串
        font_width, font_height = font.getsize(text)
        draw.text(((width - font_width) / number, (height - font_height) / number), text, font=font,
                  fill=fontcolor)  # 填充字符串
        if draw_line:
            gen_line(draw, width, height)
        # image = image.transform((width+30,height+10), Image.AFFINE, (1,-0.3,0,-0.1,1,0),Image.BILINEAR)  #创建扭曲
        image = image.transform((width + 20, height + 10), Image.AFFINE, (1, -0.3, 0, -0.1, 1, 0),
                                Image.BILINEAR)  # 创建扭曲
        image = image.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 滤镜，边界加强
        save_code(image, num)
        num -= 1

def save_code(image, num):
    image.save('./gen_imgs/code' + str(num) + '.png')  # 保存验证码图片

def remove_folder():
    shutil.rmtree('./gen_imgs')
    os.mkdir('./gen_imgs')

if __name__ == "__main__":
    remove_folder()
    print("gen_imgs文件夹已清空...")
    num = input("请输入生成的验证码的个数：")
    gen_code(int(num))