import sys
sys.setrecursionlimit(1000000)

def dfs(graph, cell_char_matrix, nx, ny, gx, gy, min_cnt):
    for adj_xy in graph[nx][ny]:
        temp_min_cnt = 10**8
        dfs(graph, adj_xy[0], adj_xy[1], gx, gy, temp_min_cnt)
        if (nx, ny) == (0, 0):
            min_cnt = min(min_cnt, temp_min_cnt)

def check_cell_pos(r_id, c_id, r, c):
    ret_is_in_inner = False
    horizontal_check = 0 <= c_id <= c-1
    vertical_check = 0 <= r_id <= r-1
    if horizontal_check and vertical_check:
        ret_is_in_inner = True
    return ret_is_in_inner

def create_graph(graph, cell_char_mat, r, c):

    for r_id in range(r):
        for c_id in range(c):
            adj_list = []
            if check_cell_pos(r_id - 1, c_id, r, c):
                adj_list.append((r_id - 1, c_id))
            if check_cell_pos(r_id, c_id - 1, r, c):
                adj_list.append((r_id, c_id - 1))
            if check_cell_pos(r_id + 1, c_id, r, c):
                adj_list.append((r_id + 1, c_id))
            if check_cell_pos(r_id, c_id + 1, r, c):
                adj_list.append((r_id, c_id + 1))
            if adj_list:
                graph[r_id][c_id] = adj_list

if __name__ == "__main__":
    r, c = [int(v) for v in input().split()]
    sx, sy = 0, 0
    gx, gy = r-1, c-1
    cell_char_matrix = [None] * r
    dist = [None] * r
    graph = [None] * r
    for row_id in range(r):
        cell_char_matrix[row_id] = [c for c in input()]
        dist[row_id] = [None] * c
        graph[row_id] = [None] * c

    create_graph(graph, cell_char_matrix, r, c)
    min_cnt = 10 ** 8
    dfs(graph, cell_char_matrix, sx, sy, gx, gy, min_cnt)
    if min_cnt == 10 ** 8:
        print(0)
    else:
        print(min_cnt)


#!/usr/bin/env python3

import csv
import matplotlib.pyplot as plt
import matplotlib.ticker as plticker

from typing import Sequence, Tuple, List, Optional, NamedTuple, Union
from cycler import cycler

from misc import ProgressElement
from misc import get_file_name_stat, get_file_name_stat_img

from post_common import NetInfo

def load_results(file_path: str) -> List[ProgressElement]:
    with open(file_path, 'r') as f:
        reader = csv.DictReader(f)
        return list(reader)

def get_legend_long(net_info: Union[Tuple, NetInfo]) -> str:
    if not isinstance(net_info, NetInfo):
        net_info = NetInfo(*net_info)

    af_name_ffn = net_info.af_name

    if net_info.af_name_cnn is None:
        af_name_cnn = af_name_ffn
    else:
        af_name_cnn = net_info.af_name_cnn

    if net_info.net_type == "base":
        legend = "{} CNN, {} FFN".format(af_name_cnn, af_name_ffn)
    elif net_info.net_type == "ahaf":
        legend = "{}-like AHAF CNN, {}-like AHAF FFN".format(
            af_name_cnn, af_name_ffn
        )
    elif net_info.net_type == "fuzzy_ffn":
        legend = "{} CNN, {}-like Fuzzy FFN".format(
            af_name_cnn, af_name_ffn
        )
    else:
        raise ValueError("Network type is not supported")

    if net_info.fine_tuned:
        legend = legend + ", fine-tuned"

    return legend

def get_short_af_name(orig: str) -> str:
    if orig == "Tanh":
        return "tanh"
    elif orig == "Sigmoid":
        return "σ-fn"
    else:
        return orig

def get_legend_short(net_info: Union[Tuple, NetInfo]) -> str:
    if not isinstance(net_info, NetInfo):
        net_info = NetInfo(*net_info)

    af_name_ffn = net_info.af_name

    if net_info.af_name_cnn is None:
        af_name_cnn = af_name_ffn
    else:
        af_name_cnn = net_info.af_name_cnn

    af_name_cnn = get_short_af_name(af_name_cnn)
    af_name_ffn = get_short_af_name(af_name_ffn)

    if net_info.net_type == "base":
        legend = "Base, {}, {}".format(af_name_cnn, af_name_ffn)
    elif net_info.net_type == "ahaf":
        legend = "AHAF, {}, {}".format(
            af_name_cnn, af_name_ffn
        )
    elif net_info.net_type == "fuzzy_ffn":
        legend = "Fuzzy, {}, {}".format(
            af_name_cnn, af_name_ffn
        )
    else:
        raise ValueError("Network type is not supported")

    if net_info.fine_tuned:
        legend = legend + ", tuned"

    return legend

def analyze_network(net_info: Tuple):
    file_path = get_file_name_stat(*net_info)
    results = load_results(file_path)
    base_legend = get_legend_short(net_info)

    acc = []
    loss = []

    for r in results:
        acc.append(float(r["test_acc"]) * 100.0)
        loss.append(float(r["train_loss_mean"]))

    return base_legend, acc, loss

def plot_networks(fig, nets: Sequence[Union[Tuple, NetInfo]], bw=False):
    acc_legends = []
    loss_legends = []

    monochrome = (
            cycler('color', ['black', 'grey', 'dimgray'])
            * cycler('linestyle', ['--', ':', '-.', '-'])
            * cycler('marker', ['None'])
    )

    gs = plt.GridSpec(1, 2)

    acc_fig = fig.add_subplot(gs[0, 0])
    #acc_loc = plticker.LinearLocator(numticks=10)
    #acc_fig.yaxis.set_major_locator(acc_loc)
    acc_fig.set_xlabel('epoch')
    acc_fig.set_ylabel('test accuracy, %')
    acc_fig.grid()
    if bw:
        acc_fig.set_prop_cycle(monochrome)

    loss_fig = fig.add_subplot(gs[0, 1])
    #loss_loc = plticker.LinearLocator(numticks=10)
    #loss_fig.yaxis.set_major_locator(loss_loc)
    loss_fig.set_xlabel('epoch')
    loss_fig.set_ylabel('training loss')
    loss_fig.grid()
    if bw:
        loss_fig.set_prop_cycle(monochrome)

    for net in nets:
        try:
            base_legend, acc, loss = analyze_network(net)
        except Exception as e:
            print("Exception: {}, skipped".format(e))
            continue

        n_epochs = len(acc)
        end_ep = net.epoch
        start_ep = end_ep - n_epochs

        x = tuple(range(start_ep, end_ep))

        acc_legends.append(
            base_legend
        )
        loss_legends.append(
            base_legend
        )
        acc_fig.plot(x, acc)
        loss_fig.plot(x, loss)

    acc_fig.legend(acc_legends)
    loss_fig.legend(loss_legends)

def visualize(
        net_group: str, nets: Sequence[Union[Tuple, NetInfo]], base_title=None,
        bw: bool = False
):
    fig = plt.figure(tight_layout=True, figsize=(7, 3.5))
    if base_title is not None:
        title = "{}, test accuracy and training loss".format(base_title)
        fig.suptitle(title)

    plot_networks(fig, nets, bw)

    if nets:
        #plt.show()
        plt.savefig(get_file_name_stat_img(
            net_group, nets[0].epoch, nets[0].patched, nets[0].fine_tuned
        ))

def main():
    nets_vs_ahaf = [
        NetInfo("base", "ReLU", 100, patched=False, fine_tuned=False),
        NetInfo("ahaf", "ReLU", 100, patched=False, fine_tuned=False),
        NetInfo("ahaf", "ReLU", 150, patched=True, fine_tuned=True),
        NetInfo("base", "SiLU", 100, patched=False, fine_tuned=False),
        NetInfo("ahaf", "SiLU", 100, patched=False, fine_tuned=False),
        NetInfo("ahaf", "SiLU", 150, patched=True, fine_tuned=True),
    ]

    visualize("vs_ahaf", nets_vs_ahaf, "Base VS AHAF", bw=False)

    nets_vs_fuzzy = [
        NetInfo("base", "Tanh", 100, patched=False, fine_tuned=False),
        NetInfo("fuzzy_ffn", "Tanh", 100, patched=False, fine_tuned=False),
        NetInfo("fuzzy_ffn", "Tanh", 150, patched=True, fine_tuned=True),
        NetInfo("base", "Sigmoid", 100, patched=False, fine_tuned=False),
        NetInfo("fuzzy_ffn", "Sigmoid", 100, patched=False, fine_tuned=False),
        NetInfo("fuzzy_ffn", "Sigmoid", 150, patched=True, fine_tuned=True)
    ]

    visualize("vs_fuzzy", nets_vs_fuzzy, "Base VS Fuzzy", bw=False)

if __name__ == "__main__":
    main()


try:
    import requests
except:
    from os import system
    system("pip install requests")
    import requests

from django.http import HttpResponse
from django.shortcuts import render

from .gets import *
from cart.get import u_cart
from reserve.get import *

# Create your views here.

def products_view(request, *args, **kwargs):
    thisCart = u_cart(request)

    productsRows, categories = getProductsCatrows()

    myContext = {
        "title": "Товары",
        "categories": categories
    }

    myContext["products"] = productsRows

    print(productsRows['овощи'][0].price)

    return render(request, "products.html", myContext)

def product_detail_view(request, *args, **kwargs):
    thisCart = u_cart(request)
    queryDict = request.GET
    requestedArticle = int(request.GET['article'])
    requestedObject = Product.objects.get(article=requestedArticle)
    requestedObject.amount -= getReservation(requestedArticle)

    myContext = {
        "title": f"Купить {requestedObject.title}",
        "pr": requestedObject
    }

    # requests.get(f"https://api.telegram.org/bot1225466990:AAHeSxZ66mt1sOD_0ojhUf4EpbxoVK06TAY/sendMessage?chat_id=@dchadm&text=Кто-то%20заказал:%20{requestedObject.title}")

    return render(request, "product_details.html", myContext)

def add_to_cart(request, *args, **kwargs):
    thisCart = u_cart(request)
    queryDict = request.GET

    if not (request.method == "GET") or not ('article' in queryDict):
        return HttpResponse("Иди куда подальще!")

    try:
        requestedArticle = int(request.GET['article'])
        requestedAmount = int (request.GET['amount'])
        requestedObject = Product.objects.get(article=requestedArticle)
        if requestedAmount > requestedObject.amount:
            return HttpResponse("Нет такого количества товаров!")
    except:
        return HttpResponse("Bad request!")

    newAmount = requestedObject.amount - requestedAmount
    whetherAvailable = True
    if newAmount == 0:
        whetherAvailable = False

    return HttpResponse(requestedObject.title)


from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.http import HttpResponse

from myapp.forms import TaskForm
from .models import Task
# Create your views here.
def home(request):
    tasks =Task.objects.all()
    all_task = tasks.count()

    completed = Task.objects.filter(status=True)
    completed_task = completed.count()

    Incompleted = Task.objects.filter(status=False)
    Incompleted_task = Incompleted.count()

    form = TaskForm
    if request.method =='POST':
        form = TaskForm(request.POST)
        if form.is_valid():
            form.save()
        return redirect('/')

    context = {'tasks':tasks,'form':form, 'all_task':all_task,'completed':completed_task,'Incompleted':Incompleted_task}
    return render(request ,'home.html', context)

def deleteTask(request,pk):
    item = Task.objects.get(id=pk)
    if request.method=='POST':
        item.delete()
        return redirect('home')
    context={
        'item':item
    }
    return render(request,'delete.html')

def update_Task(request,pk):
    task = Task.objects.get(id=pk)

    form = TaskForm(instance=task)

    if request.method=='POST':
        form = TaskForm(request.POST,instance=task)
        form.save()
        return redirect('home')

    context ={
        'form':form
    }

    return render(request, 'update.html', context)


"""Helper function to get input from user side with video detection"""
from __future__ import annotations

import argparse

def parse_arguments() -> argparse[object]:
    """Function to parse the arguments"""
    ap = argparse.ArgumentParser()

    ap.add_argument("-i", "--input", type=str, required=True,
                    help="Path to optional input video file")

    ap.add_argument("-m", "--model", required=True,
                    help="Path to yolo model")

    args = vars(ap.parse_args())
    return args


import sys, os
import time

import getAllHostIPonLAN
###
### global variables
###
doDebug = False

###
### function define.
###

###
### Tester
###
def main():
	argc = len(sys.argv)
	if argc != 3:
		print("Error command")
		print("usage: {} <ip segement> <STDOUT_flag>".format(sys.argv[0]))
		print("\tlike: {} 192.168.1.0/24 ON(or OFF)".format(sys.argv[0]))
		sys.exit(1)

	## else, check ip segement Enter right!
	print("ip segement: {}".format(sys.argv[1]))

	## STDOUT_flag = 'OFF/ON'
	STDOUT_flag = sys.argv[2]

	networkTool = getAllHostIPonLAN.NetworkTool()
	segement = sys.argv[1]
	allIPAddr = networkTool.getAllHostIPonLAN(segement)

	if STDOUT_flag == "ON":
		print(allIPAddr)
	else:
		try:
			with open("/tmp/allLANHostIP.txt", "w") as fp:
				for eachIP in allIPAddr:
					print("{}".format(eachIP), file=fp)
		except:
			print("something wrong when print out Result!", file=sys.stderr)
#END main().

if __name__ == '__main__':
	main()


from math import degrees, cos, sin
from statistics import mean
from calendar import c
import os
import cv2
import time
import sys
import signal
from PIL import Image, ImageDraw, ImageFont
from PyQt5 import QtGui, QtCore, QtWidgets
from six import BytesIO
import paho.mqtt.client as mqtt
import simplejpeg
import imagezmq
import numpy as np
import time
import classes
import json
from collections import deque
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure
import matplotlib.pyplot as plt

pcutoff=0.5
pixel_to_mm = 0.00005
MQTT_SERVER="raspberrypi"
IMAGEZMQ='raspberrypi'
TARGET=sys.argv[1]
PORT=sys.argv[2]
XY_STEP_SIZE=100
XY_FEED=50

Z_STEP_SIZE=15
Z_FEED=1
keys = (QtCore.Qt.Key_0, QtCore.Qt.Key_1, QtCore.Qt.Key_2, QtCore.Qt.Key_3, QtCore.Qt.Key_4, QtCore.Qt.Key_5, QtCore.Qt.Key_6, QtCore.Qt.Key_7, QtCore.Qt.Key_8, QtCore.Qt.Key_9)
MovementKeys=(QtCore.Qt.Key_Left, QtCore.Qt.Key_Right, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown)
colormap = [QtCore.Qt.red, QtCore.Qt.green, QtCore.Qt.blue, QtCore.Qt.yellow, QtCore.Qt.magenta, QtCore.Qt.black]

counter = 0

fontScale = 1
color = (255, 0, 0)
thickness = 2
font = cv2.FONT_HERSHEY_SIMPLEX

class ImageZMQCameraReader(QtCore.QThread):
    imageSignal = QtCore.pyqtSignal(np.ndarray)
    #predictSignal = QtCore.pyqtSignal(list)
    def __init__(self):
        super(ImageZMQCameraReader, self).__init__()
        url = f"tcp://{IMAGEZMQ}:{PORT}"
        print("Connect to url", url)
        self.image_hub = imagezmq.ImageHub(url, REQ_REP=False)
    def run(self):
        name, jpg_buffer = self.image_hub.recv_jpg()
        image_data = simplejpeg.decode_jpeg( jpg_buffer, colorspace='RGB')

        while True:
            name, jpg_buffer = self.image_hub.recv_jpg()
            image_data = simplejpeg.decode_jpeg( jpg_buffer, colorspace='RGB')

            self.imageSignal.emit(image_data)

class ControlWindow(QtWidgets.QWidget):
    def __init__(self, window):
        super().__init__()
        self.window = window

        layout = QtWidgets.QVBoxLayout()
        self.setLayout(layout)

class MplWindow(QtWidgets.QWidget):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.figure = Figure(figsize=(1, 1), dpi=100)
        self.canvas = FigureCanvasQTAgg(self.figure)
        self.axes = self.figure.add_subplot(111)

        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.canvas)
        self.setLayout(self.layout)

class Window(QtWidgets.QLabel):
    def eventFilter(self, widget, event):
        if isinstance(event, QtGui.QKeyEvent):
            if not event.isAutoRepeat():
                key = event.key()

                if key == QtCore.Qt.Key_Left:
                    if event.type() == QtCore.QEvent.KeyRelease:
                        self.client.publish(f"{TARGET}/cancel")
                    elif event.type() == QtCore.QEvent.KeyPress:
                        self.client.publish(f"{TARGET}/cancel")
                        cmd = f"$J=G91 G21 F{XY_FEED:.3f} X{XY_STEP_SIZE:.3f}"
                        self.client.publish(f"{TARGET}/command", cmd)
                elif key == QtCore.Qt.Key_Right:
                    if event.type() == QtCore.QEvent.KeyRelease:
                        self.client.publish(f"{TARGET}/cancel")
                    elif event.type() == QtCore.QEvent.KeyPress:
                        self.client.publish(f"{TARGET}/cancel")
                        cmd = f"$J=G91 G21 F{XY_FEED:.3f} X-{XY_STEP_SIZE:.3f}"
                        self.client.publish(f"{TARGET}/command", cmd)
                elif key == QtCore.Qt.Key_Up:
                    if event.type() == QtCore.QEvent.KeyRelease:
                        self.client.publish(f"{TARGET}/cancel")
                    elif event.type() == QtCore.QEvent.KeyPress:
                        self.client.publish(f"{TARGET}/cancel")
                        cmd = f"$J=G91 G21 F{XY_FEED:.3f} Y-{XY_STEP_SIZE:.3f}"
                        self.client.publish(f"{TARGET}/command", cmd)
                elif key == QtCore.Qt.Key_Down:
                    if event.type() == QtCore.QEvent.KeyRelease:
                        self.client.publish(f"{TARGET}/cancel")
                    elif event.type() == QtCore.QEvent.KeyPress:
                        self.client.publish(f"{TARGET}/cancel")
                        cmd = f"$J=G91 G21 F{XY_FEED:.3f} Y{XY_STEP_SIZE:.3f}"
                        self.client.publish(f"{TARGET}/command", cmd)
                elif key == QtCore.Qt.Key_Plus:
                    if event.type() == QtCore.QEvent.KeyRelease:
                        self.client.publish(f"{TARGET}/cancel")
                    elif event.type() == QtCore.QEvent.KeyPress:
                        self.client.publish(f"{TARGET}/cancel")
                        cmd = f"$J=G91 G21 F{Z_FEED:.3f} Z-{Z_STEP_SIZE:.3f}"
                        self.client.publish(f"{TARGET}/command", cmd)
                elif key == QtCore.Qt.Key_Minus:
                    if event.type() == QtCore.QEvent.KeyRelease:
                        self.client.publish(f"{TARGET}/cancel")
                    elif event.type() == QtCore.QEvent.KeyPress:
                        self.client.publish(f"{TARGET}/cancel")
                        cmd = f"$J=G91 G21 F{Z_FEED:.3f} Z{Z_STEP_SIZE:.3f}"
                        self.client.publish(f"{TARGET}/command", cmd)

        return super().eventFilter(widget, event)

    def __init__(self, mpl_window):
        super(Window, self).__init__()

        self.installEventFilter(self)

        self.mpl_window = mpl_window

        #self.resize(640,480)
        self.camera = ImageZMQCameraReader()
        self.camera.start()
        self.camera.imageSignal.connect(self.imageTo)

        self.client =  mqtt.Client()
        self.client.on_connect = self.on_connect
        self.client.on_disconnect = self.on_disconnect
        self.client.on_message = self.on_message
        self.client.connect(MQTT_SERVER)
        self.client.loop_start()
        self.outstanding = 0

        self.positions = {}
        self.connected = False

        self.m_pos = None
        self.w_pos = None

        self.time = None
        self.state = "Unknown"

        self.results = None
        self.one = 50
        self.two = 100

        self.old_image = None

        self.queue = deque()

        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.timerExpired)
        self.timer.start(1000)

    def timerExpired(self, *args):
        if self.connected:
            self.client.publish(f"{TARGET}/command", "?")

    def on_message(self, client, userdata, message):
        if message.topic == f"{TARGET}/m_pos":
            self.m_pos = eval(message.payload)
        # elif message.topic == f"{TARGET}/inference":
        #     self.results = json.loads(message.payload)
        elif message.topic == f"{TARGET}/state":
            self.state = message.payload.decode('utf-8')

    def on_connect(self, client, userdata, flags, rc):
        print("connected")
        self.connected = True
        self.client.subscribe(f"{TARGET}/m_pos")
        #self.client.subscribe(f"{TARGET}/inference")
        self.client.subscribe(f"{TARGET}/state")

    def on_disconnect(self, client, userdata, flags):
        print("disconnected")
        self.connected = False

    def mousePressEvent(self, event):
        # Compute delta from c_pos to middle of window, then scale by pixel size
        s_pos = QtCore.QPoint(self.size().width()/2, self.size().height()/2)
        cursor_offset = QtCore.QPointF(event.pos()-s_pos)*pixel_to_mm*30
        cmd = "$J=G91  G21 X%.3f Y%.3f F%.3f"% (-cursor_offset.x(), cursor_offset.y(), XY_FEED)
        self.client.publish(f"{TARGET}/command", cmd)

    def imageTo(self, image_data):
        draw_data = image_data

        image = QtGui.QImage(draw_data, draw_data.shape[1], draw_data.shape[0], QtGui.QImage.Format_RGB888)

        if self.m_pos is not None:
            p = QtGui.QPainter()

            p.begin(image)
            p.setCompositionMode( QtGui.QPainter.CompositionMode_SourceOver )
            p.setRenderHints( QtGui.QPainter.HighQualityAntialiasing )

            #p.drawImage(QtCore.QPoint(), image)

            pen = QtGui.QPen(QtCore.Qt.red)
            pen.setWidth(2)
            p.setPen(pen)

            font = QtGui.QFont()
            font.setFamily('Times')
            font.setBold(True)
            font.setPointSize(24)
            p.setFont(font)

            p.drawText(0, 50, self.state)
            p.drawText(0, 100, "X%8.3fmm" % self.m_pos[0])
            p.drawText(0, 150, "Y%8.3fmm" % self.m_pos[1])
            p.drawText(0, 200, "Z%8.3fmm" % self.m_pos[2])
            p.end()

        pixmap = QtGui.QPixmap.fromImage(image)#.scaled(QtWidgets.QApplication.instance().primaryScreen().size(), QtCore.Qt.KeepAspectRatio)
        self.resize(pixmap.size().width(), pixmap.size().height())
        self.setPixmap(pixmap)

if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    app = QtWidgets.QApplication(sys.argv)

    sc = MplWindow()
    sc.show()
    window = Window(sc)

    cw = ControlWindow(window)
    window.show()#FullScreen()
    cw.show()

    app.exec_()


from django.conf.urls import url
from django.contrib.auth.views import login
from . import views

app_name="klucz"
urlpatterns =[
    url(r'^$', views.login_view, name='logowanie'),
    url(r'potwierdzenie', views.potwierdzenie, name='potwierdzenie'),
    url(r'klucz', views.wybierz_kluczowe, name='wybierz_kluczowe'),
    url(r'^login/$', login, {'template_name': 'login2.html'}),

    url(r'^error/$', login, {'template_name': 'login_error.html'}),

]

#url(r'^$', views.wybierz_kluczowe, name='wybierz_kluczowe'),


# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re

class SeleniumEntrenamiento(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.implicitly_wait(30)
        self.base_url = "http://127.0.0.1:8000/"
        self.verificationErrors = []
        self.accept_next_alert = True

    def test_selenium_entrenamiento(self):
        driver = self.driver
        driver.get(self.base_url + "/EigenFaces/trainer/")
        driver.find_element_by_id("procesarCentroide").click()
        driver.find_element_by_id("procesarCentroide").click()
        # ERROR: Caught exception [ERROR: Unsupported command [waitForFrameToLoad |  | ]]
        driver.find_element_by_name("file").click()
        # Warning: waitForTextPresent may require manual changes
        for i in range(60):
            try:
                if re.search(r"^[\s\S]*1-1\.pgm[\s\S]*$", driver.find_element_by_css_selector("BODY").text): break
            except: pass
            time.sleep(1)
        else: self.fail("time out")
        driver.find_element_by_id("procesarCentroide").click()
        # ERROR: Caught exception [ERROR: Unsupported command [waitForFrameToLoad |  | ]]
        driver.find_element_by_id("procesarCentroide").click()
        # ERROR: Caught exception [ERROR: Unsupported command [waitForFrameToLoad | Seleccione un archivo. | ]]

    def is_element_present(self, how, what):
        try: self.driver.find_element(by=how, value=what)
        except NoSuchElementException as e: return False
        return True

    def is_alert_present(self):
        try: self.driver.switch_to_alert()
        except NoAlertPresentException as e: return False
        return True

    def close_alert_and_get_its_text(self):
        try:
            alert = self.driver.switch_to_alert()
            alert_text = alert.text
            if self.accept_next_alert:
                alert.accept()
            else:
                alert.dismiss()
            return alert_text
        finally: self.accept_next_alert = True

    def tearDown(self):
        self.driver.quit()
        self.assertEqual([], self.verificationErrors)

if __name__ == "__main__":
    unittest.main()

Avance 3/Pruebas de Sistema/Pruebas Entrenamiento/SeleniumEntrenamiento.py


import torch

import torch.nn as nn

from embed_regularize import embedded_dropout
from locked_dropout import LockedDropout
from weight_drop import WeightDrop

class RNNModel(nn.Module):
    """Container module with an encoder, a recurrent module, and a decoder."""

    def __init__(self, rnn_type, vocab, ninp, nhid, nlayers, dropout=0.5, dropouth=0.5, dropouti=0.5, dropoute=0.1, wdrop=0, tie_weights=False, locked=True, padding_idx=None):
        super(RNNModel, self).__init__()

        self.lockdrop = LockedDropout()
        self.drop = self._lock_drop if locked else self._nonlock_drop

        self.encoder = nn.Embedding(vocab.size, ninp, padding_idx=padding_idx)
        assert rnn_type in ['LSTM', 'QRNN', 'GRU'], 'RNN type is not supported'
        if rnn_type == 'LSTM':
            self.rnns = [torch.nn.LSTM(ninp if l == 0 else nhid, nhid if l != nlayers - 1 else (ninp if tie_weights else nhid), 1, dropout=0, batch_first=True) for l in range(nlayers)]
            if wdrop:
                self.rnns = [WeightDrop(rnn, ['weight_hh_l0'], dropout=wdrop) for rnn in self.rnns]
        if rnn_type == 'GRU':
            self.rnns = [torch.nn.GRU(ninp if l == 0 else nhid, nhid if l != nlayers - 1 else ninp, 1, dropout=0, batch_first=True) for l in range(nlayers)]
            if wdrop:
                self.rnns = [WeightDrop(rnn, ['weight_hh_l0'], dropout=wdrop) for rnn in self.rnns]
        elif rnn_type == 'QRNN':
            from torchqrnn import QRNNLayer
            self.rnns = [QRNNLayer(input_size=ninp if l == 0 else nhid, hidden_size=nhid if l != nlayers - 1 else (ninp if tie_weights else nhid), save_prev_x=True, zoneout=0, window=2 if l == 0 else 1, output_gate=True) for l in range(nlayers)]
            for rnn in self.rnns:
                rnn.linear = WeightDrop(rnn.linear, ['weight'], dropout=wdrop)

        self.rnns = torch.nn.ModuleList(self.rnns)
        self.decoder = nn.Linear(nhid, vocab.size)

        # Optionally tie weights as in:
        # "Using the Output Embedding to Improve Language Models" (Press & Wolf 2016)
        # https://arxiv.org/abs/1608.05859
        # and
        # "Tying Word Vectors and Word Classifiers: A Loss Framework for Language Modeling" (Inan et al. 2016)
        # https://arxiv.org/abs/1611.01462
        if tie_weights:
            #if nhid != ninp:
            #    raise ValueError('When using the tied flag, nhid must be equal to emsize')
            self.decoder.weight = self.encoder.weight

        self.init_weights()

        self.rnn_type = rnn_type
        self.vocab = vocab
        self.ninp = ninp
        self.nhid = nhid
        self.nlayers = nlayers
        self.dropout = dropout
        self.dropouti = dropouti
        self.dropouth = dropouth
        self.dropoute = dropoute
        self.tie_weights = tie_weights

    def _lock_drop(self, w, ratio):
        return self.lockdrop(w, ratio)

    def _nonlock_drop(self, w, ratio):
        d = nn.Dropout(ratio)
        d(w)
        return w

    def reset(self):
        if self.rnn_type == 'QRNN': [r.reset() for r in self.rnns]

    def init_weights(self):
        initrange = 0.1
        self.encoder.weight.data.uniform_(-initrange, initrange)
        self.decoder.bias.data.fill_(0)
        self.decoder.weight.data.uniform_(-initrange, initrange)

    def forward(self, input, hidden=None, return_h=False, input_lengths=None):
        """
        Apply a multi-layer RNN to an input sequence.

        If input_lengths is given, variable-length inputs are assumed. The input to
        RNN is packed in this case. This method does not sort the input by length
        internally, so input_var should be sorted beforehand.
        """
        emb = embedded_dropout(
            self.encoder, input, dropout=self.dropoute if self.training else 0)

        #emb = self.lockdrop(emb, self.dropouti)
        emb = self.drop(emb, self.dropouti)
        raw_output = emb

        if not hidden:
            hidden = self.init_hidden(input.size(0)) # batch_first

        new_hidden = []
        #raw_output, hidden = self.rnn(emb, hidden)
        raw_outputs = []
        outputs = []
        # enumerate layers
        for l, rnn in enumerate(self.rnns):
            if input_lengths is not None:
                raw_output = nn.utils.rnn.pack_padded_sequence(
                    raw_output, input_lengths, batch_first=True)
            raw_output, new_h = rnn(raw_output, hidden[l])
            new_hidden.append(new_h)
            if input_lengths is not None:
                raw_output, _ = nn.utils.rnn.pad_packed_sequence(
                    raw_output, batch_first=True)
            raw_outputs.append(raw_output)
            if l != self.nlayers - 1:
                # self.hdrop(raw_output)
                raw_output = self.drop(raw_output, self.dropouth)
                # raw_output = self.lockdrop(raw_output, self.dropouth)
                outputs.append(raw_output)
        hidden = new_hidden

        # output = self.lockdrop(raw_output, self.dropout)
        output = self.drop(raw_output, self.dropout)
        outputs.append(output)

        result = output.contiguous().view(
            output.size(0)*output.size(1), output.size(2))
        if return_h:
            return result, hidden, raw_outputs, outputs
        return result, hidden

    def init_hidden(self, bsz):
        weight = next(self.parameters()).data
        if self.rnn_type == 'LSTM':
            return [(weight.new(1, bsz, self.nhid if l != self.nlayers - 1 else (self.ninp if self.tie_weights else self.nhid)).zero_(),
                     weight.new(1, bsz, self.nhid if l != self.nlayers - 1 else (self.ninp if self.tie_weights else self.nhid)).zero_())
                    for l in range(self.nlayers)]
        elif self.rnn_type == 'QRNN' or self.rnn_type == 'GRU':
            return [weight.new(1, bsz, self.nhid if l != self.nlayers - 1 else (self.ninp if self.tie_weights else self.nhid)).zero_()
                    for l in range(self.nlayers)]


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Dec 13 10:14:29 2023

@author: patricksweeney
"""

#%% Import
def import_data():
    import pandas as pd
    file_path = '/Users/patricksweeney/growth/02_Pricing/06_Migration/Migration.xlsx'
    data = pd.read_excel(file_path)
    return data

data = import_data()

#%% Data checks
def data_checks(data):
    import pandas as pd

    # Initialize message list
    messages = []

    # Check for missing values and list columns with missing values
    for col in data.columns:
        if data[col].isnull().any():
            messages.append(f"Variable '{col}': Contains missing values")

    # Automatically set variables to int, float, or category
    for col in data.columns:
        original_dtype = data[col].dtype
        try:
            # Try converting to int
            data[col] = pd.to_numeric(data[col], downcast='integer')
        except ValueError:
            try:
                # Try converting to float
                data[col] = pd.to_numeric(data[col], downcast='float')
            except ValueError:
                # Convert to category if other conversions fail
                if original_dtype == object:
                    data[col] = data[col].astype('category')
                else:
                    messages.append(f"Variable '{col}': Cannot be converted to int, float, or category")

    # Return message or a success message if no issues are found
    return "\n".join(messages) if messages else "Data passes all checks."

data_checks(data)

#%% Choose varibales

def declare_variables(data, package, p1, v1, p2):
    # Select the specified columns and create a new DataFrame
    # Rename the columns to p1, v1, and v2
    new_data = data[[package, p1, v1, p2]].rename(columns={p1: 'p1', v1: 'v1', p2: 'p2'})

    return new_data

# Example usage
data = declare_variables(data, 'product', 'arpu', 'seats_latest', 'new_arpu')

#%% Summarise

def summarise_numbers(data):
    """
    Summarize key financial metrics from the dataset, with results rounded to the nearest integer.

    Parameters:
    - data (DataFrame): The dataset containing the variables p1, v1.

    Returns:
    - Dictionary containing the summary statistics, rounded to the nearest integer.
    """

    # Calculate Total Revenue
    total_revenue = round((data['p1'] * data['v1']).sum())

    # Count Total Customers
    total_customers = data.shape[0]

    # Calculate ARPA (Average Revenue Per Account)
    arpa = round(total_revenue / total_customers) if total_customers > 0 else 0

    # Sum Total Seats
    total_seats = round(data['v1'].sum())

    # Calculate ARPU (Average Revenue Per Unit)
    arpu = round(total_revenue / total_seats) if total_seats > 0 else 0

    return {
        'Total Revenue': total_revenue,
        'Total Customers': total_customers,
        'ARPA': arpa,
        'Total Seats': total_seats,
        'ARPU': arpu
    }
# Example usage:
summarise_numbers(data)

#%% Empirical survival function

def plot_empirical_survival_function_with_volume(data, product_column):
    import numpy as np
    import matplotlib.pyplot as plt
    import pandas as pd

    """
    Plot the survival function of price for each product, taking into account the volume (v1) for each price (p1).
    If a product column is specified, creates a separate subplot for each product.

    Parameters:
    - data (DataFrame): The dataset containing the variables p1 (price) and v1 (volume), and optionally a product column.
    - product_column (str): The name of the column containing product identifiers. If None, plots for the entire dataset.

    Returns:
    - Subplots of the price survival function for each product.
    """

    if product_column and product_column in data.columns:
        products = data[product_column].unique()
        num_products = len(products)
        fig, axes = plt.subplots(num_products, 1, figsize=(10, 6 * num_products))

        for i, product in enumerate(products):
            product_data = data[data[product_column] == product]
            expanded_prices = np.repeat(product_data['p1'], product_data['v1'])
            sorted_prices = np.sort(expanded_prices)[::-1]
            survival_rate = np.arange(1, len(sorted_prices) + 1) / len(sorted_prices)

            ax = axes[i] if num_products > 1 else axes
            ax.plot(sorted_prices, survival_rate, label=f'Price Survival Function for {product}')
            ax.set_xlabel('Price (p1)')
            ax.set_ylabel('Survival Rate')
            ax.set_title(f'Survival Function of Price for {product}')
            ax.legend()
            ax.grid(True)
    else:
        # Expand the price data by volume
        expanded_prices = np.repeat(data['p1'], data['v1'])
        sorted_prices = np.sort(expanded_prices)[::-1]
        survival_rate = np.arange(1, len(sorted_prices) + 1) / len(sorted_prices)

        plt.figure(figsize=(10, 6))
        plt.plot(sorted_prices, survival_rate, label='Price Survival Function with Volume')
        plt.xlabel('Price (p1)')
        plt.ylabel('Survival Rate')
        plt.title('Survival Function of Price with Volume Consideration')
        plt.legend()
        plt.grid(True)
        plt.show()

# Example usage:
plot_empirical_survival_function_with_volume(data, product_column='product')

#%%

def generalized_gamma_prf(data, product_column):
    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.stats import gengamma

    """
    Estimate and plot a Generalized Gamma survival function, price elasticity, and revenue curve for price data with volume consideration,
    adjusting for non-positive values, for each product, and print the model parameters.
    """

    if product_column and product_column in data.columns:
        products = data[product_column].unique()
        num_products = len(products)
        fig, axes = plt.subplots(num_products, 3, figsize=(45, 8 * num_products))

        for i, product in enumerate(products):
            product_data = data[data[product_column] == product]
            expanded_prices = np.repeat(product_data['p1'], product_data['v1'])
            df_for_gengamma = pd.DataFrame(expanded_prices, columns=['p1'])
            df_for_gengamma['p1'] = df_for_gengamma['p1'].clip(lower=0.001)

            # Fit a Generalized Gamma model
            params = gengamma.fit(df_for_gengamma['p1'])
            a, c, loc, scale = params

            prices = np.linspace(df_for_gengamma['p1'].min(), df_for_gengamma['p1'].max(), 100)
            survival_function = gengamma.sf(prices, a, c, loc=loc, scale=scale)

            # Subplot for survival function
            ax1 = axes[i, 0]
            ax1.plot(prices, survival_function, label=f'GG Survival Function for {product}')
            ax1.set_xlabel('Price (p1)')
            ax1.set_ylabel('Survival Probability')
            ax1.legend(loc='upper right')
            ax1.set_title(f'Generalized Gamma Survival Function for {product}')

            # Calculate and plot price elasticity (based on hazard rate)
            ax2 = axes[i, 1]
            hazard_rate = gengamma.pdf(prices, a, c, loc=loc, scale=scale) / survival_function
            elasticity = hazard_rate * prices
            ax2.plot(prices, elasticity, label=f'Price Elasticity for {product}', color='red')
            ax2.set_xlabel('Price (p1)')
            ax2.set_ylabel('Price Elasticity')
            ax2.legend(loc='upper right')
            ax2.set_title(f'Price Elasticity for {product}')

            # Subplot for revenue curve
            ax3 = axes[i, 2]
            revenue = prices * (1 - survival_function)
            ax3.plot(prices, revenue, label=f'Revenue for {product}', color='blue')
            ax3.set_xlabel('Price (p1)')
            ax3.set_ylabel('Revenue')
            ax3.legend(loc='upper right')
            ax3.set_title(f'Revenue Curve for {product}')

            print(f"Product: {product}")
            print("Generalized Gamma Model Parameters:")
            print(f"Shape parameter (a): {a:.4f}")
            print(f"Scale parameter (c): {c:.4f}")
            print(f"Location parameter: {loc:.4f}")
            print(f"Scale parameter: {scale:.4f}")
            print("\n")

        plt.tight_layout()
        plt.show()

    else:
        # Expand the price data by volume and ensure all prices are positive
        expanded_prices = np.repeat(data['p1'], data['v1'])
        df_for_gengamma = pd.DataFrame(expanded_prices, columns=['p1'])
        df_for_gengamma['p1'] = df_for_gengamma['p1'].clip(lower=0.001)

        # Fit a Generalized Gamma model
        params = gengamma.fit(df_for_gengamma['p1'])
        a, c, loc, scale = params

        prices = np.linspace(df_for_gengamma['p1'].min(), df_for_gengamma['p1'].max(), 100)
        survival_function = gengamma.sf(prices, a, c, loc=loc, scale=scale)

        # Plotting
        plt.figure(figsize=(15, 6))

        # Plot survival function
        ax1 = plt.subplot(1, 3, 1)
        ax1.plot(prices, survival_function, label='GG Survival Function with Volume')
        ax1.set_xlabel('Price (p1)')
        ax1.set_ylabel('Survival Probability')
        ax1.legend(loc='upper left')

        # Calculate and plot price elasticity (based on hazard rate)
        ax2 = plt.subplot(1, 3, 2)
        hazard_rate = gengamma.pdf(prices, a, c, loc=loc, scale=scale) / survival_function
        elasticity = hazard_rate * prices
        ax2.plot(prices, elasticity, label='Price Elasticity', color='red')
        ax2.set_xlabel('Price (p1)')
        ax2.set_ylabel('Price Elasticity')
        ax2.legend(loc='upper right')

        # Plot revenue curve
        ax3 = plt.subplot(1, 3, 3)
        revenue = prices * (1 - survival_function)
        ax3.plot(prices, revenue, label='Revenue Curve', color='blue')
        ax3.set_xlabel('Price (p1)')
        ax3.set_ylabel('Revenue')
        ax3.legend(loc='upper right')

        # Print model parameters
        print("Generalized Gamma Model Parameters:")
        print(f"Shape parameter (a): {a:.4f}")
        print(f"Scale parameter (c): {c:.4f}")
        print(f"Location parameter: {loc:.4f}")
        print(f"Scale parameter: {scale:.4f}")

        plt.tight_layout()
        plt.show()

# Example usage
generalized_gamma_prf(data, product_column='product')

#%% Fit an exponential function with lifelines
def exponential_prf(data, product_column):
    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    from lifelines import ExponentialFitter

    """
    Estimate and plot an exponential survival function, log-elasticity, hazard rate, and revenue curve for price data with volume consideration,
    adjusting for non-positive values, for each product, and print the model parameters.

    Parameters:
    - data (DataFrame): The dataset containing the variables p1 (price), v1 (volume), and optionally a product column.
    - product_column (str): The name of the column containing product identifiers. If None, plots for the entire dataset.

    Returns:
    - Subplots of the estimated exponential survival function, log-elasticity, hazard rate, and revenue curve for each product.
    - Prints the model parameters for each product.
    """

    if product_column and product_column in data.columns:
        products = data[product_column].unique()
        num_products = len(products)
        fig, axes = plt.subplots(num_products, 4, figsize=(50, 8 * num_products))  # Increase number of subplots to 5

        for i, product in enumerate(products):
            product_data = data[data[product_column] == product]
            expanded_prices = np.repeat(product_data['p1'], product_data['v1'])
            df_for_lifelines = pd.DataFrame(expanded_prices, columns=['p1'])
            df_for_lifelines['p1'] = df_for_lifelines['p1'].clip(lower=0.001)
            df_for_lifelines['event_observed'] = 1

            # Calculate empirical survival function
            df_sorted = df_for_lifelines.sort_values(by='p1', ascending=False)
            empirical_survival = np.cumsum(df_sorted['event_observed']) / len(df_sorted)

            # Plot empirical survival function
            ax0 = axes[i, 0]
            ax0.step(df_sorted['p1'], empirical_survival, where="post", label=f'Empirical Survival for {product}')
            ax0.set_xlabel('Price (p1)')
            ax0.set_ylabel('Empirical Survival Probability')
            ax0.legend(loc='upper right')
            ax0.set_title(f'Empirical Survival Function for {product}')

            exp_fit = ExponentialFitter()
            exp_fit.fit(df_for_lifelines['p1'], event_observed=df_for_lifelines['event_observed'])
            lambda_value = 1 / exp_fit.lambda_

            # Subplot for survival function
            ax1 = axes[i, 0]
            exp_fit.survival_function_.plot(ax=ax1, label=f'Exp Survival Function for {product}')
            ax1.set_xlabel('Price (p1)')
            ax1.set_ylabel('Survival Probability')
            ax1.legend(loc='upper right')
            ax1.set_title(f'Exponential Survival Function for {product}')

            # Subplot for log-elasticity
            ax2 = axes[i, 1]
            prices = np.linspace(df_for_lifelines['p1'].min(), df_for_lifelines['p1'].max(), 100)
            log_elasticity = abs(-lambda_value * (prices))
            ax2.plot(prices, log_elasticity, label=f'Log-Elasticity for {product}', color='red', linestyle='--')
            ax2.set_xlabel('Price (p1)')
            ax2.set_ylabel('Log-Elasticity')
            ax2.legend(loc='upper right')
            ax2.set_title(f'Log-Elasticity for {product}')

            # Calculate and plot the distribution of WTP
            ax3 = axes[i, 2]
            wtp_distribution = lambda_value * np.exp(-lambda_value * prices)
            ax3.plot(prices, wtp_distribution, label=f'WTP Distribution for {product}', color='green')
            ax3.set_xlabel('Price (p1)')
            ax3.set_ylabel('Density')
            ax3.legend(loc='upper right')
            ax3.set_title(f'WTP Distribution for {product}')

            # Subplot for revenue curve
            ax4 = axes[i, 3]
            revenue = prices * np.exp(-lambda_value * prices)  # Revenue based on exponential survival function
            ax4.plot(prices, revenue, label=f'Revenue for {product}', color='blue')
            ax4.set_xlabel('Price (p1)')
            ax4.set_ylabel('Revenue')
            ax4.legend(loc='upper right')
            ax4.set_title(f'Revenue Curve for {product}')

            print(f"Product: {product}")
            print("Exponential Model Parameters:")
            print(f"Lambda (rate parameter): {lambda_value:.4f}")
            print(f"Log-likelihood: {exp_fit.log_likelihood_:.4f}")
            print("\n")

        plt.tight_layout()
        plt.show()

    else:
        # Expand the price data by volume and ensure all prices are positive
        expanded_prices = np.repeat(data['p1'], data['v1'])
        df_for_lifelines = pd.DataFrame(expanded_prices, columns=['p1'])
        df_for_lifelines['p1'] = df_for_lifelines['p1'].clip(lower=0.001)
        df_for_lifelines['event_observed'] = 1

        # Fit an exponential model
        exp_fit = ExponentialFitter()
        exp_fit.fit(df_for_lifelines['p1'], event_observed=df_for_lifelines['event_observed'])
        lambda_value = 1 / exp_fit.lambda_

        # Plotting
        plt.figure(figsize=(15, 6))

        # Plot survival function
        ax1 = plt.subplot(1, 4, 1)
        exp_fit.survival_function_.plot(ax=ax1, label='Exponential Survival Function with Volume')
        ax1.set_xlabel('Price (p1)')
        ax1.set_ylabel('Survival Probability')
        ax1.legend(loc='upper left')

        # Calculate and plot log-elasticity
        ax2 = plt.subplot(1, 4, 2)
        prices = np.linspace(df_for_lifelines['p1'].min(), df_for_lifelines['p1'].max(), 100)
        log_elasticity = -lambda_value * np.log(prices)
        ax2.plot(prices, log_elasticity, label='Log-Elasticity', color='red', linestyle='--')
        ax2.set_xlabel('Price (p1)')
        ax2.set_ylabel('Log-Elasticity')
        ax2.legend(loc='upper right')

        # Plot hazard rate
        ax3 = plt.subplot(1, 4, 3)
        ax3.axhline(y=lambda_value, color='green', linestyle='-.')
        ax3.set_xlabel('Price (p1)')
        ax3.set_ylabel('Hazard Rate')
        ax3.set_title('Constant Hazard Rate')
        ax3.legend(['Constant Hazard Rate'], loc='upper right')

        # Plot revenue curve
        ax4 = plt.subplot(1, 4, 4)
        revenue = prices * np.exp(-lambda_value * prices)
        ax4.plot(prices, revenue, label='Revenue Curve', color='blue')
        ax4.set_xlabel('Price (p1)')
        ax4.set_ylabel('Revenue')
        ax4.legend(loc='upper right')

        # Print model parameters
        print("Exponential Model Parameters:")
        print(f"Lambda (rate parameter): {lambda_value:.4f}")
        print(f"Log-likelihood: {exp_fit.log_likelihood_:.4f}")

        plt.tight_layout()
        plt.show()

# Example usage
exponential_prf(data, product_column='product')

#%% Weibull with distribution of WTP
def weibull_prf(data, product_column=None):
    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    from lifelines import WeibullFitter

    """
    Estimate and plot a Weibull survival function, elasticity, hazard rate, and revenue curve for price data with volume consideration,
    adjusting for non-positive values, for each product, and print the model parameters.

    Parameters:
    - data (DataFrame): The dataset containing the variables p1 (price), v1 (volume), and optionally a product column.
    - product_column (str, optional): The name of the column containing product identifiers.

    Returns:
    - Subplots of the estimated Weibull survival function, elasticity, hazard rate, and revenue curve for each product.
    - Prints the model parameters for each product.
    """

    if product_column and product_column in data.columns:
        products = data[product_column].unique()
        num_products = len(products)
        fig, axes = plt.subplots(num_products, 4, figsize=(40, 8 * num_products))  # Increased number of subplots to 4

        weibull_params = {}
        for i, product in enumerate(products):
            product_data = data[data[product_column] == product]
            expanded_prices = np.repeat(product_data['p1'], product_data['v1'])
            df_for_lifelines = pd.DataFrame({'price': expanded_prices})
            df_for_lifelines['price'] = df_for_lifelines['price'].clip(lower=0.001)
            df_for_lifelines['event_observed'] = 1

            weibull_fit = WeibullFitter()
            weibull_fit.fit(df_for_lifelines['price'], event_observed=df_for_lifelines['event_observed'])
            lambda_param = weibull_fit.lambda_
            rho_param = weibull_fit.rho_

            weibull_params[product] = (weibull_fit.lambda_, weibull_fit.rho_)

            # Calculate empirical survival function
            df_sorted = df_for_lifelines.sort_values(by='price', ascending=False)
            empirical_survival = np.cumsum(df_sorted['event_observed']) / len(df_sorted)

            # Subplot for Weibull survival function with empirical overlay
            ax1 = axes[i, 0]
            weibull_fit.survival_function_.plot(ax=ax1, label=f'Weibull Survival Function for {product}')
            ax1.step(df_sorted['price'], empirical_survival, where="post", label=f'Empirical Survival for {product}', linestyle='--', color='orange')
            ax1.set_xlabel('Price (p1)')
            ax1.set_ylabel('Survival Probability')
            ax1.legend(loc='upper right')
            ax1.set_title(f'Weibull Survival Function for {product}')

            # Subplot for survival function
            ax1 = axes[i, 0]
            weibull_fit.survival_function_.plot(ax=ax1, label=f'Weibull Survival Function for {product}')
            ax1.set_xlabel('Price (p1)')
            ax1.set_ylabel('Survival Probability')
            ax1.legend(loc='upper right')
            ax1.set_title(f'Weibull Survival Function for {product}')

            # Subplot for elasticity
            ax2 = axes[i, 1]
            prices = np.linspace(df_for_lifelines['price'].min(), df_for_lifelines['price'].max(), 100)
            absolute_elasticity = abs(-rho_param * (prices / lambda_param) ** (rho_param))
            ax2.plot(prices, np.abs(absolute_elasticity), label=f'Absolute Elasticity for {product}', color='red', linestyle='--')
            ax2.set_xlabel('Price (p1)')
            ax2.set_ylabel('Absolute Elasticity')
            ax2.legend(loc='upper right')
            ax2.set_title(f'Absolute Elasticity for {product}')

            # Subplot for WTP distribution
            ax3 = axes[i, 2]
            prices_pdf = np.linspace(df_for_lifelines['price'].min(), df_for_lifelines['price'].max(), 100)
            weibull_pdf = (rho_param / lambda_param) * (prices_pdf / lambda_param)**(rho_param - 1) * np.exp(-(prices_pdf / lambda_param)**rho_param)
            ax3.plot(prices_pdf, weibull_pdf, label=f'WTP Distribution for {product}', color='green')
            ax3.set_xlabel('Price (p1)')
            ax3.set_ylabel('Density')
            ax3.legend(loc='upper right')
            ax3.set_title(f'WTP Distribution for {product}')

            # Find the price where absolute elasticity is approximately 1
            unit_elasticity_price = prices[np.argmin(np.abs(absolute_elasticity - 1))]

            # Subplot for revenue curve
            ax4 = axes[i, 3]
            revenue = prices * weibull_fit.survival_function_at_times(prices).values
            ax4.plot(prices, revenue, label=f'Revenue for {product}', color='blue')
            ax4.axvline(x=unit_elasticity_price, color='grey', linestyle='--', label='Unit Elasticity Price')
            ax4.set_xlabel('Price (p1)')
            ax4.set_ylabel('Revenue')
            ax4.legend(loc='upper right')
            ax4.set_title(f'Revenue Curve for {product}')

            print(f"Product: {product}")
            print("Weibull Model Parameters:")
            print(f"Lambda (scale parameter): {lambda_param:.4f}")
            print(f"Rho (shape parameter): {rho_param:.4f}")
            print(f"Log-likelihood: {weibull_fit.log_likelihood_:.4f}")
            print("\n")

        plt.tight_layout()
        plt.show()

    else:
        # Handling single product case
        expanded_prices = np.repeat(data['p1'], data['v1'])
        df_for_lifelines = pd.DataFrame({'price': expanded_prices})
        df_for_lifelines['price'] = df_for_lifelines['price'].clip(lower=0.001)
        df_for_lifelines['event_observed'] = 1

        weibull_fit = WeibullFitter()
        weibull_fit.fit(df_for_lifelines['price'], event_observed=df_for_lifelines['event_observed'])
        lambda_param = weibull_fit.lambda_
        rho_param = weibull_fit.rho_

        # Plotting
        plt.figure(figsize=(20, 6))

        # Plot survival function
        ax1 = plt.subplot(141)
        weibull_fit.survival_function_.plot(ax=ax1, label='Weibull Survival Function with Volume')
        ax1.set_xlabel('Price (p1)')
        ax1.set_ylabel('Survival Probability')
        ax1.legend(loc='upper left')

        # Calculate and plot elasticity
        ax2 = plt.subplot(142)
        prices = np.linspace(df_for_lifelines['price'].min(), df_for_lifelines['price'].max(), 100)
        elasticity = rho_param * (prices / lambda_param) ** (rho_param - 1)
        ax2.plot(prices, elasticity, label='Absolute Elasticity', color='red', linestyle='--')
        ax2.set_xlabel('Price (p1)')
        ax2.set_ylabel('Elasticity')
        ax2.legend(loc='upper right')

        # Plot hazard rate
        ax3 = plt.subplot(143)
        hazard_rate = weibull_fit.hazard_.values.flatten()
        ax3.plot(weibull_fit.hazard_.index, hazard_rate, label='Hazard Rate', color='green')
        ax3.set_xlabel('Price (p1)')
        ax3.set_ylabel('Hazard Rate')
        ax3.legend(loc='upper right')

        # Plot revenue curve
        ax4 = plt.subplot(144)
        revenue = prices * weibull_fit.survival_function_.values.flatten()[:100]
        ax4.plot(prices, revenue, label='Revenue Curve', color='blue')
        ax4.set_xlabel('Price (p1)')
        ax4.set_ylabel('Revenue')
        ax4.legend(loc='upper right')

        # Print model parameters
        print("Weibull Model Parameters:")
        print(f"Lambda (scale parameter): {lambda_param:.4f}")
        print(f"Rho (shape parameter): {rho_param:.4f}")
        print(f"Log-likelihood: {weibull_fit.log_likelihood_:.4f}")

        plt.tight_layout()
        plt.show()

    return weibull_params

# Example usage
weibull_params = weibull_prf(data, product_column='product')

#%%

#%% Exponential survival fucntion

def calculate_new_revenue(data, p1, v1, p2, weibull_params, product_column):
    import numpy as np
    # Initialize new columns
    data['v2'] = np.nan
    data['direction'] = 'No change'
    data['elasticity'] = np.nan
    data['r1'] = data[p1] * data[v1]
    data['r2'] = np.nan
    data['lift'] = np.nan

    for product in data[product_column].unique():
        lambda_param, rho_param = weibull_params.get(product, (None, None))
        if lambda_param and rho_param:
            product_data = data[data[product_column] == product]
            new_volume = product_data[v1] * np.exp(-((product_data[p2] / lambda_param) ** rho_param - (product_data[p1] / lambda_param) ** rho_param))
            data.loc[data[product_column] == product, 'v2'] = new_volume

            # Calculate direction
            data.loc[data[product_column] == product, 'direction'] = np.where(product_data[p2] > product_data[p1], 'Increase',
                                                                               np.where(product_data[p2] < product_data[p1], 'Decrease', 'No change'))

            # Calculate elasticity
            price_ratio = np.log(product_data[p2] / product_data[p1])
            volume_ratio = np.log(new_volume / product_data[v1])
            data.loc[data[product_column] == product, 'elasticity'] = np.abs(volume_ratio / price_ratio)

            # Calculate new revenue and lift
            data.loc[data[product_column] == product, 'r2'] = product_data[p2] * new_volume
            # Calculate arithmetic lift instead of log lift
            data.loc[data[product_column] == product, 'lift'] = (data.loc[data[product_column] == product, 'r2'] - data.loc[data[product_column] == product, 'r1']) / data.loc[data[product_column] == product, 'r1'].replace(0, np.nan)

    # Handle cases where price or revenue doesn't change to avoid division by zero
    data.loc[data['direction'] == 'No change', 'elasticity'] = np.nan
    data.replace([np.inf, -np.inf], np.nan, inplace=True)

    return data

# Example usage
# Assuming 'data' is a predefined DataFrame and 'weibull_params' is a predefined dictionary
new_data = calculate_new_revenue(data, 'p1', 'v1', 'p2', weibull_params, 'product')

#%% Save

#%%

def calculate_impact(new_data):
    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np

    # Ensure the data types are correct for calculations
    new_data[['p1', 'v1', 'p2', 'v2']] = new_data[['p1', 'v1', 'p2', 'v2']].apply(pd.to_numeric, errors='coerce')

    # Replace all zeros in p1, p2, v1, and v2 with a small value
    small_value = 0.01
    new_data['p1'] = new_data['p1'].replace(0, small_value)
    new_data['p2'] = new_data['p2'].replace(0, small_value)
    new_data['v1'] = new_data['v1'].replace(0, small_value)
    new_data['v2'] = new_data['v2'].replace(0, small_value)

    # Initial Calculations
    new_data['r1'] = new_data['p1'] * new_data['v1']
    new_data['r2'] = new_data['p2'] * new_data['v2']
    new_data['lift'] = (new_data['r2'] - new_data['r1']) / new_data['r1'].replace(0, np.nan)
    new_data['elasticity'] = np.abs((new_data['v2'] - new_data['v1']) / new_data['v1']) / np.abs((new_data['p2'] - new_data['p1']) / new_data['p1'])

    # Revenue Impact
    total_revenue_diff = new_data['r2'].sum() - new_data['r1'].sum()
    percent_revenue_diff = total_revenue_diff / new_data['r1'].sum()

    new_data['elasticity'] = np.abs((new_data['v2'] - new_data['v1']) / new_data['v1'].replace(0, np.nan)) / np.abs((new_data['p2'] - new_data['p1']) / new_data['p1'].replace(0, np.nan))
    new_data['elasticity'].replace([np.inf, -np.inf], np.nan, inplace=True)

    # Portfolio Lift and Volatility
    total_revenue = new_data['r1'].sum()
    new_data['weight'] = new_data['r1'] / total_revenue
    portfolio_lift = (new_data['lift'] * new_data['weight']).sum()
    portfolio_volatility = np.sqrt((new_data['weight'] * (new_data['lift'] - portfolio_lift)**2).sum())
    sharpe_ratio = portfolio_lift / portfolio_volatility if portfolio_volatility != 0 else np.nan

    # APRA Impact
    apra_diff = new_data['r2'].mean() - new_data['r1'].mean()
    percent_apra_diff = apra_diff / new_data['r1'].mean()

    # Volume Impact
    volume_diff = (new_data['v2'] > 1).sum() - (new_data['v1'] > 1).sum()
    percent_volume_diff = volume_diff / (new_data['v1'] > 1).sum()

    # Seats Impact
    seats_diff = volume_diff
    percent_seats_diff = percent_volume_diff

    # ARPU Impact
    arpu_diff = new_data['r2'].mean() - new_data['r1'].mean()
    percent_arpu_diff = percent_apra_diff

    # Plot PMF of direction column
    if 'direction' in new_data.columns:
        direction_pmf = new_data['direction'].value_counts(normalize=True)
        plt.figure()
        direction_pmf.plot(kind='bar')
        plt.title('Direction Probability Mass Function')
        plt.ylabel('Percentage')
        plt.xlabel('Direction')

    # Creating subplots for distributions
    fig, axs = plt.subplots(2, 2, figsize=(10, 10))  # 2x2 grid of subplots

    # Distribution of Revenue Lift
    axs[0, 0].hist(new_data['lift'].replace([np.inf, -np.inf], np.nan).dropna(), bins=20, edgecolor='black', color='blue')
    axs[0, 0].set_title('Distribution of Revenue Lift')
    axs[0, 0].set_xlabel('Lift')
    axs[0, 0].set_ylabel('Frequency')

    # Distribution of Elasticity
    axs[0, 1].hist(new_data['elasticity'].dropna(), bins=20, edgecolor='black', color='green')
    axs[0, 1].set_title('Distribution of Elasticity')
    axs[0, 1].set_xlabel('Elasticity')
    axs[0, 1].set_ylabel('Frequency')

    # Distribution of Price Change (expressed as a percentage)
    price_change = 100 * np.abs((new_data['p2'] - new_data['p1']) / new_data['p1'])
    axs[1, 0].hist(price_change.dropna(), bins=20, edgecolor='black', color='red')
    axs[1, 0].set_title('Distribution of Price Change (%)')
    axs[1, 0].set_xlabel('Price Change (%)')
    axs[1, 0].set_ylabel('Frequency')

    # Distribution of Volume Change (expressed as a percentage)
    volume_change = 100 * np.abs((new_data['v2'] - new_data['v1']) / new_data['v1'])
    axs[1, 1].hist(volume_change.dropna(), bins=20, edgecolor='black', color='purple')
    axs[1, 1].set_title('Distribution of Volume Change (%)')
    axs[1, 1].set_xlabel('Volume Change (%)')
    axs[1, 1].set_ylabel('Frequency')

    plt.tight_layout()  # Adjusts the subplots to fit into the figure area.
    plt.show()

    # Recommendation: Go / No-Go
    recommendation = 'Go' if total_revenue_diff > 0 else 'No-Go'

    # Formatting results
    results = {
        'Total Revenue Difference': f"${total_revenue_diff:,.0f}",
        'Percent Revenue Difference': f"{percent_revenue_diff:.1%}",
        '\nPortfolio Lift': f"{portfolio_lift * 100:.1f}%",
        'Portfolio Volatility': f"{portfolio_volatility * 100:.1f}%",
        'Sharpe Ratio': f"{sharpe_ratio:.2f}",
        '\nAPRA Difference': f"${apra_diff:,.0f}",
        'Percent APRA Difference': f"{percent_apra_diff:.1%}",
        '\nVolume Difference': f"{volume_diff:,d}",
        'Percent Volume Difference': f"{percent_volume_diff:.1%}",
        '\nSeats Difference': f"{int(seats_diff):,d}",
        'Percent Seats Difference': f"{percent_seats_diff:.1%}",
        '\nARPU Difference': f"${arpu_diff:,.0f}",
        'Percent ARPU Difference': f"{percent_arpu_diff:.1%}",
        '\nRecommendation': recommendation
    }

    return results

# Example usage
impact_results = calculate_impact(new_data)
for key, value in impact_results.items():
    print(f"{key}: {value}")

#%%

def find_risks(data):
    import pandas as pd

    # Filter for rows with price increases
    price_increase_data = data[data['p2'] > data['p1']]

    # Calculate percentage changes
    price_increase_data['price_change_pct'] = 100 * (price_increase_data['p2'] - price_increase_data['p1']) / price_increase_data['p1']
    price_increase_data['volume_change_pct'] = 100 * (price_increase_data['v2'] - price_increase_data['v1']) / price_increase_data['v1']
    price_increase_data['revenue_change_pct'] = 100 * (price_increase_data['r2'] - price_increase_data['r1']) / price_increase_data['r1']

    # Sort by price change percentage in descending order
    sorted_data = price_increase_data.sort_values(by='price_change_pct', ascending=False)

    # Format the results
    formatted_results = sorted_data.apply(
        lambda row: f"${row['p1']} -> ${row['p2']} (+{row['price_change_pct']:.1f}%): ({row['volume_change_pct']:.1f}% volume, {row['revenue_change_pct']:.1f}% revenue)",
        axis=1
    )

    return formatted_results

# Example usage
risk_list = find_risks(new_data)
print(risk_list)

#%%

def save_excel(data, filename='new_data.xlsx'):
    import pandas as pd

    # Save the DataFrame to an Excel file
    data.to_excel(filename, index=False)

# Example usage
new_data = calculate_new_revenue(data, 'p1', 'v1', 'p2', weibull_params, 'product')
save_excel(new_data, 'new_revenue_data.xlsx')


#!/usr/bin/python3
import random
number = random.randint(-10000, 10000)
if number >= 0:
    helper = number % 10
else:
    helper = -number % 10 * -1
print("Last digit of {:d} is {:d}".format(number, helper), end=" ")
if helper > 5:
    print("and is greater than 5")
elif helper < 6 and helper != 0:
    print("and is less than 6 and not 0")
else:
    print("and is 0")


import arrow
from decimal import Decimal  # If you want to use money this works better

# for floats 0.01 +0.02 != 0.03
import numpy as np
import numpy_financial as npf
from pathlib import Path
from scipy import stats
import shutil

import xlsxwriter
import importlib.metadata

# This is the controlling model which has all the constants in it
#  The various other modules then use this to do
# - capex
# - drug dosage
# - simulation run
class CoreModel:
    def __init__(self):
        self.num_years = 15
        # Defaults for spreadsheet
        self.wb = None
        self.ws = None  # For base case and mean
        self.ws_sd = None  # For Standard Deviation MC
        # Define formats in case not using ws and so not defined later
        self.bold = None
        self.money_fmt = None
        self.bold_money_fmt = None

        self.write_base = True
        try:
            self.version = importlib.metadata.version("pyxmc")
        except importlib.metadata.PackageNotFoundError:
            self.version = "V0.0.0 Alpha"

        self.xlsx_name = "pyxmc.xlsx"
        self.copy_xlsx_path = None

    def create_xlsx(self, filename=None):
        if filename:
            self.xlsx_name = filename
        self.wb = xlsxwriter.Workbook(f"{self.xlsx_name}")
        self.scenario = ""
        self.bold = self.wb.add_format({"bold": 1})
        self.highlight_fmt = self.wb.add_format({"bold": 1, "bg_color": "yellow"})
        self.money_fmt = self.wb.add_format({"num_format": "#,##0"})
        self.bold_money_fmt = self.wb.add_format({"bold": 1, "num_format": "#,##0"})
        # Used on first run to mark where
        self.row = 0

    def close_xlsx(self):
        self.wb.close()
        my_file = Path(self.xlsx_name)
        if self.copy_xlsx_path:
            to_file = Path(self.copy_xlsx_path)
            try:
                shutil.copy(my_file, to_file)
            except PermissionError:
                print("Excel probably open so can't copy over")

    def add_worksheet(self, tabname, colour="green"):
        ws = self.wb.add_worksheet(tabname)
        ws.set_tab_color(colour)
        ws.set_column(0, 0, 25)
        ws.set_column(
            1, self.num_years + 1, 10
        )  # Col B used for single valued data eg NPV
        ws.write_string(0, 0, self.version)
        self.add_years(ws, 1)
        if self.row is None or self.row < 3:
            self.row = 4  # Singleton to mark number of rows used by header
        return ws

    def add_years(self, ws, row):
        ws.write_string(row + 1, 0, "Years", self.bold)
        base_year = arrow.utcnow().shift(years=+1).year
        for i in range(self.num_years):
            ws.write_number(row, i + 2, i + base_year)
            ws.write_number(row + 1, i + 2, i, self.bold)


#!/usr/bin/env python
# encoding=utf-8
# maintainer: rgaudin

import re

from datetime import date, timedelta

from bolibana.models import Provider

def contact_for(identity):
    try:
        provider = Provider.objects.get(phone_number=identity)
    except Provider.DoesNotExist:
        provider = None

    return provider

def resp_error(message, action):
    message.respond(u"[ERREUR] Impossible de comprendre " \
                    u"le SMS pour %s" % action)
    return True

def resp_error_date(message):
    message.respond(u"[Date de visite] la date n'est pas valide.")
    return True

def conv_str_int(value):
    try:
        value = int(value)
    except:
        value = None
    return value

def resp_error_dob(message):
    message.respond(u"[ERREUR] la date de naissance n'est pas valide")
    return True

def resp_error_provider(message):
    message.respond(u"Aucun utilisateur ne possede ce numero de telephone")
    return True

def parse_age_dob(age_or_dob, only_date=False):
    """ parse argument as date or age. return date and bool if estimation """

    if re.match(r'^\d{8}$', age_or_dob):
        auto = False
        parsed_date = date(int(age_or_dob[0:4]), int(age_or_dob[4:6]), \
                           int(age_or_dob[6:8]))
    else:
        auto = True
        today = date.today()
        unit = age_or_dob[-1]
        value = int(age_or_dob[:-1])
        if unit.lower() == 'a':
            parsed_date = today - timedelta(365 * value) - timedelta(160)
        elif unit.lower() == 'm':
            parsed_date = today - timedelta(30 * value) - timedelta(15)
        else:
            raise ValueError(u"Age unit unknown: %s" % unit)

    if only_date:
        return parsed_date
    else:
        return (parsed_date, auto)

def date_is_old(reporting_date):

    if (date.today() - reporting_date).days > 30:
        raise ValueError(u"Le %s est passé il y a plus 30 jours" % reporting_date)


import cv2
import os
import numpy as np
import qrcode
import random

# 1) Считываем все фотографии из директории
dir_path = "images"
all_images = [os.path.join(dir_path, img) for img in os.listdir(dir_path) if img.endswith(('jpg', 'png'))]

# Создаем QR-код
data = "https://t.me/burmistrov_robotics"
box_size = 27  # Это начальное значение, вы можете изменить его позже для настройки разрешения

qr = qrcode.QRCode(
    version=1,
    error_correction=qrcode.constants.ERROR_CORRECT_L,
    box_size=box_size,
    border=1
)
qr.add_data(data)
qr.make(fit=True)

# Вычисляем размер модуля в зависимости от размера итогового изображения
final_image_side = 4050
# Отношение сторон итогового изображения к количеству модулей в QR коде
scale_factor = final_image_side / (len(qr.modules) * box_size)

# Теперь создаем изображение с учетом масштабного коэффициента
img_qr = qr.make_image(fill_color="black", back_color="white").resize((int(scale_factor * len(qr.modules) * box_size), int(scale_factor * len(qr.modules) * box_size)))

# Если вы хотите сохранить QR-код в файл для проверки:
img_qr.save("high_res_qr.png")

# Открываем файл с QR-кодом
img_qr = cv2.imread('high_res_qr.png')

# Задаем параметры
box_size_pixels = 150
modules_count = 27  # так как 27x27 квадратов

# Проходим по всем квадратам
for y in range(modules_count):
    for x in range(modules_count):
        # Извлекаем квадрат из изображения
        square = img_qr[y*box_size_pixels:(y+1)*box_size_pixels, x*box_size_pixels:(x+1)*box_size_pixels]
        # Вычисляем средний цвет квадрата
        module_color = np.mean(square)
        if module_color < 128:  # Если модуль черный
            # Выбираем случайное изображение
            random_img_path = random.choice(all_images)
            img = cv2.imread(random_img_path)
            brightness_offset = 0  # Можете выбрать любое другое значение
            img = cv2.subtract(img, np.ones_like(img) * brightness_offset)
            # Ограничиваем значения пикселей, чтобы они оставались в диапазоне [0, 255]
            img = np.clip(img, 0, 255)
            h, w, _ = img.shape
            min_edge = min(h, w)
            yoff = (h - min_edge) // 2
            xoff = (w - min_edge) // 2
            squared = img[yoff:yoff+min_edge, xoff:xoff+min_edge]
            squared_resized = cv2.resize(squared, (box_size_pixels, box_size_pixels))
            # Вставляем изображение вместо черного квадрата
            img_qr[y*box_size_pixels:(y+1)*box_size_pixels, x*box_size_pixels:(x+1)*box_size_pixels] = squared_resized

# Сохраняем результирующее изображение
cv2.imwrite('final_qr_collage.png', img_qr)

# Конвертировать изображение в градации серого
gray_qr = cv2.cvtColor(img_qr, cv2.COLOR_BGR2GRAY)

# Сохранить бинаризованное изображение
cv2.imwrite('final_qr_collage_bw.png', gray_qr)


# https://leetcode.cn/problems/max-consecutive-ones/

class Solution:
    '''
        给定一个二进制数组 nums ， 计算其中最大连续 1 的个数。
        eg：
        输入：nums = [1,1,0,1,1,1]
        输出：3
        解释：开头的两位和最后的三位都是连续 1 ，所以最大连续 1 的个数是 3.
        思路：

    '''
    def findMaxConsecutiveOnes(self, nums: list):
        maxCnt = cnt = 0

        for i in nums:
            if i == 1:
                cnt += 1
            else:
                maxCnt = max(maxCnt, cnt)
                cnt = 0
        maxCnt = max(maxCnt, cnt)   # 最后出现连续的 1 且长度最大时
        return maxCnt

nums = [0, 0, 0, 0]
s = Solution()
res = s.findMaxConsecutiveOnes(nums)
print(res)

1.数组/最大连续 1 的个数.py

最大连续 1 的个数.py


import bpy
from bpy.props import StringProperty
import re

matching_props = []

bpy.types.Object.ice_cube_search_filter = StringProperty(
    name="ice_cube_search_filter",
    description="Search filter for Ice Cube",
    default="",
    options={'TEXTEDIT_UPDATE'}
    )

def ic_search_ui(self,context,scale):
    obj = context.object
    search_input = obj.ice_cube_search_filter
    layers = context.active_object.data

    material_list = {}
    materials = bpy.data.materials
    for mat in materials:
        try:
            if mat["ice_cube_material"]:
                material_list[mat["ice_cube_material"]] = mat
        except KeyError:
            pass

    layout = self.layout
    box = layout.box()
    b = box.row(align=True)
    b.scale_y = scale
    b.label(text="Search Results")
    b = box.row(align=True)
    b.scale_y = scale
    search_results_box = b.box()

    search_reference = {
    }

    def sep(search_name,property_name,display_name="",index=0,expand=True,toggle=True,slider=True,source=obj):
        search_reference[search_name] = {"display_name": display_name if display_name else search_name, "property_name": property_name, "expand": expand, "toggle": toggle, "slider": slider, "index": index, "source": source, "type":"prop"}

    def seo(search_name,op_name,display_name=""):

        search_reference[search_name] = {"display_name": display_name if display_name else search_name,"op_name":op_name, "type":"op"}

    def sem(search_name,node,material_slot,input,mat_id,display_name=""):

        search_reference[search_name] = {"display_name": display_name if display_name else search_name,"node":node, "material_slot": material_slot, "input": input, "mat_id":mat_id, "type":"mat"}

    # Search Entries
    entries = (
        #rig style props
        sep("Smooth Bend Sharp Bend Type Style","bendstyle","Bend Type"),
        sep("Arm Type Style Steve Alex Thin Model","armtype_enum"),
        sep("Right Fingers","fingers_r"),
        sep("Left Fingers","fingers_l"),
        sep("Face Rig Enable Hide","facerig","Face Rig"),
        sep("Mouth Type Style Square Mouth Mine-imator Mouth Mineimator Mouth","mouthtypes","Mouth Style"),
        sep("Eyelashes Female Woman Girl","eyelashes","Eyelashes",toggle=False),
        sep("Mouth Rotate","mouthrotate",toggle=False),
        sep("Tongue Mouth","tongue","Tongue",toggle=False),
        sep("Cartoon Line Lips Mouth","line_mouth","Cartoon Mouth",toggle=False),
        sep("Dynamic Hair","dynamichair",toggle=False),
        sep("Cartoon Teeth Mouth","teeth_cartoon","Cartoon Teeth"),
        sep("Boolean Teeth Clipping Head","teeth_bool","Teeth Boolean"),
        sep("Teeth Curve Cartoon","teeth_curve","Teeth Curve"),
        sep("Teeth Follow Mouth","teeth_follow","Teeth Follow"),
        sep("Mouth Bevel Lip","bevelmouth","Mouth Bevel"),
        sep("Mouth Bevel Lip Strength","bevelmouthstrength","Mouth Bevel Strength"),
        sep("Jaw Enable Disable","jaw","Jaw Toggle"),
        sep("Jaw Round Smooth Curve","round_jaw","Round Jaw"),
        sep("Jaw Strength Length","jaw_strength","Jaw Strength"),
        sep("Eye Depth 2D","eyedepth","Eye Depth"),
        sep("Mouth Depth 2D","mouthdepth","Mouth Depth"),
        sep("Inner Mouth Depth 2D Clipping Inside","innermouthdepth","Inner Mouth Depth"),
        sep("Thumbfill Right","thumbfill_R"),
        sep("Thumbfill Left","thumbfill_L"),

        #mesh style
        sep("Body Bulge Round Deform","bodybulge","Body Bulge"),
        sep("Arm Right Bulge Round Deform","bulge_arm_r","Right Arm Bulge"),
        sep("Arm Left Bulge Round Deform","bulge_arm_l","Left Arm Bulge"),
        sep("Leg Right Bulge Round Deform","bulge_leg_r","Right Leg Bulge"),
        sep("Leg Left Bulge Round Deform","bulge_leg_l","Left Leg Bulge"),
        sep("Head Squish Deform","squish_head","Head Squish"),
        sep("Body Squish Deform","squish_body","Body Squish"),
        sep("Right Arm Squish Deform","squish_arm_r","Right Arm Squish"),
        sep("Left Arm Squish Deform","squish_arm_l","Left Arm Squish"),
        sep("Right Leg Squish Deform","squish_leg_r","Right Leg Squish"),
        sep("Left Leg Squish Deform","squish_leg_l","Left Leg Squish"),
        sep("Upper Arm Taper Deform","armtaper","Upper Arms"),
        sep("Lower Arm Taper Deform","armtaperlower","Lower Arms"),
        sep("Upper Leg Taper Deform","leg_taper_strength2","Upper Legs"),
        sep("Lower Leg Taper Female Woman Girl Deform","leg_taper_strength","Lower Legs"),
        sep("Hips Female Woman Girl Deform","hip","Hip"),
        sep("Upper Body Female Woman Girl Deform","upperbodywidth","Upper Width"),
        sep("Rounded Body Top Deform","bodytopround","Rounded Body Top"),
        sep("Lower Body Width Fat Deform","lowerbodywidth","Lower Body Width"),
        sep("Chest Size Boobs Breasts Female Woman Girl Deform","breastsize","Chest Size"),
        sep("Chest Weight Boobs Breasts Female Woman Girl Deform","breastweight","Chest Weight"),
        sep("Chest Shape Boobs Breasts Female Woman Girl Deform","breastshape","Chest Shape"),
        sep("Eyebrow Height","eyebrowheight"),
        sep("Eyebrow Length","eyebrowlength"),
        sep("Inner Taper Eyebrow","eyebrowtaper1"),
        sep("Outer Taper Eyebrow","eyebrowtaper2"),

        #controls
        sep("Anti Lag Laggy Slow Performance Stutter", "antilag", "Anti-Lag",toggle=False),
        sep("Wireframe","wireframe",toggle=False),
        sep("Global Head Rotation Animation","global_head_rotation","Global Head Rotation",toggle=False),
        sep("Eye Tracker Control","eyetracker","Eye Tracker",toggle=False),
        sep("Toggle Custom 1","toggle_1","Toggle 1",toggle=False),
        sep("Toggle Custom 2","toggle_2","Toggle 2",toggle=False),
        sep("Toggle Custom 3","toggle_3","Toggle 3",toggle=False),
        sep("Toggle Custom 4","toggle_4","Toggle 4",toggle=False),
        sep("Right Arm IK Inverse Kinematics","r_arm_ik","Right Arm IK"),
        sep("Left Arm IK Inverse Kinematics","l_arm_ik","Left Arm IK"),
        sep("Right Leg IK Inverse Kinematics","r_leg_ik","Right Leg IK"),
        sep("Left Leg IK Inverse Kinematics","l_leg_ik","Left Leg IK"),
        sep("IK Stretch Arm Right","stretch_arm_r","Right Arm Stretch"),
        sep("IK Stretch Arm Left","stretch_arm_l","Left Arm Stretch"),
        sep("IK Stretch Leg Right","stretch_leg_r","Right Leg Stretch"),
        sep("IK Stretch Leg Left","stretch_leg_l","Left Leg Stretch"),
        sep("Wrist Lock IK Right","wrist_lock_r","Right Wrist Lock"),
        sep("Wrist Lock IK Left","wrist_lock_l","Left Wrist Lock"),
        sep("Ankle IK Right","ankle_r","Right Ankle"),
        sep("Ankle IK Left","ankle_l","Left Ankle"),
        sep("Arm IK Parent Right","arm_ik_parent_r","IK Parent R",expand=False),
        sep("Arm IK Parent Left","arm_ik_parent_l","IK Parent L",expand=False),
        sep("Eye Influence Controls","eye_influence","Eye Influence"),
        sep("Eyebrow Influence Controls","eyebrow_influence","Eyebrow Influence"),
        sep("Mouth Influence Controls","mouth_influence","Mouth Influence"),

        #bone layers
        sep("Main Rig Bone Layer","layers",source=layers,index=0),
        sep("Face Panel Bone Layer","layers",source=layers,index=23),
        sep("Right Arm IK Bone Layer","layers",source=layers,index=1),
        sep("Left Arm IK Bone Layer","layers",source=layers,index=2),
        sep("Right Arm FK Bone Layer","layers",source=layers,index=17),
        sep("Left Arm FK Bone Layer","layers",source=layers,index=18),
        sep("Right Fingers Bone Layer","layers",source=layers,index=5),
        sep("Left Fingers Bone Layer","layers",source=layers,index=21),
        sep("Right Leg IK Bone Layer","layers",source=layers,index=3),
        sep("Left Leg IK Bone Layer","layers",source=layers,index=4),
        sep("Right Leg FK Bone Layer","layers",source=layers,index=19),
        sep("Left Leg FK Bone Layer","layers",source=layers,index=20),
        sep("Left Leg FK Bone Layer","layers",source=layers,index=20),
        sep("Body Tweak","layers",source=layers,index=7),
        sep("Face Tweak","layers",source=layers,index=16),
        sep("Right Arm Tweak","layers",source=layers,index=8),
        sep("Left Arm Tweak","layers",source=layers,index=9),
        sep("Right Leg Tweak","layers",source=layers,index=24),
        sep("Left Leg Tweak","layers",source=layers,index=25),
        sep("Limb Twist","layers",source=layers,index=10),
        sep("Extras","layers",source=layers,index=22),
        sep("Emotion Bones","layers",source=layers,index=15),
        sep("Dynamic Hair","layers",source=layers,index=6),
        sep("Footroll","layers",source=layers,index=26),
        sep("Cartoon Mouth","layers",source=layers,index=27),

        #skins
        sep("Minecraft Skin Downloader Username Downloader Skin Search NameMC","minecraft_username","Username",source=context.scene),
        seo("Minecraft Skin Downloader Username Downloader Skin Search NameMC Button","skin.download","Download From Username"),
        sem("Auto SSS Skin","color",0,1,"skin","Auto SSS"),
        sem("Auto SSS Sensitivity Skin","color",0,3,"skin","Auto SSS Sensitivity"),
        sem("Auto SSS Value Skin","color",0,4,"skin","SSS"),
        sem("Auto SSS Radius Skin","color",0,7,"skin","SSS Radius"),
        sem("Base Skin Color Colour SSS","color",0,2,"skin","Skin Color"),
        sem("SSS Color Colour","color",0,5,"skin","SSS Color"),

        #eyes
        sep("Gradient Eyes Color Colour Switch Eye Type Texture Eyes","gradient_color_eye","Eye Type"),
        sem("Eye Color Colour 1 Iris","eyenode",1,0,"eyes","Eye Color 1"),
        sem("Eye Color Colour 3 Iris","eyenode",1,2,"eyes","Eye Color 2"),
        sem("Eye Color Colour 2 Iris","eyenode",1,1,"eyes","Eye Color 3"),
        sem("Eye Color Colour 4 Iris","eyenode",1,3,"eyes","Eye Color 4"),
        sem("Specular Eye Color Colour","eyenode",1,4,"eyes","Eye Specular"),
        sem("Roughness Eye Color Colour","eyenode",1,5,"eyes","Eye Roughness"),
        sep("Emission Toggle Eye","toggleemission","Toggle Emission"),
        sep("Emission Toggle Pupil","togglepupil","Toggle Pupil"),
        sem("Pupil Color Colour 1 Iris","eyenode",1,11,"eyes","Pupil Color 1"),
        sem("Pupil Color Colour 2 Iris","eyenode",1,26,"eyes","Pupil Color 2"),
        sem("Pupil Size Iris","eyenode",1,10,"eyes"),
        sep("Pupil Bright Eye Iris","pupil_bright","Pupil Bright"),
        sep("Pupil Flip Bright Eye Iris","flip_pupil_bright","Flip Pupil Bright"),
        sem("Pupil Size 2 Iris","eyenode",1,24,"eyes"),
        sep("Toggle Sparkle Eye 1","togglesparkle1","Toggle Sparkle 1"),
        sep("Toggle Sparkle Eye 2","togglesparkle2","Toggle Sparkle 2"),
        sem("Sparkle 1 Size","eyenode",1,14,"eyes"),
        sem("Sparkle 1 Pos","eyenode",1,15,"eyes"),
        sem("Sparkle 2 Size","eyenode",1,18,"eyes"),
        sem("Sparkle 2 Pos","eyenode",1,19,"eyes"),

        #misc mats
        sem("Eyewhites Color Colour Eye R1","Principled BSDF",10,0,"eyewhites_r1","Eyewhite Color 1"),
        sem("Eyewhites Color Colour Eye L1","Principled BSDF",2,0,"eyewhites_l1","Eyewhite Color 2"),
        sem("Eyewhites Color Colour Eye R2","Principled BSDF",11,0,"eyewhites_r2","Eyewhite Color 3"),
        sem("Eyewhites Color Colour Eye L2","Principled BSDF",9,0,"eyewhites_l2","Eyewhite Color 4"),
        sem("Eyewhites Specular Color Colour Reflection","Principled BSDF",2,7,"eyewhites_l1","Eyewhite Specular"),
        sem("Eyewhites Roughness Color Colour Reflection","Principled BSDF",2,9,"eyewhites_l1","Eyewhite Rougness"),
        sem("Eyebrow Color Colour R1","Principled BSDF",13,0,"eyebrow_r1", "Eyebrow Color 1"),
        sem("Eyebrow Color Colour L1","Principled BSDF",6,0,"eyebrow_l1", "Eyebrow Color 2"),
        sem("Eyebrow Color Colour R2","Principled BSDF",14,0,"eyebrow_r2", "Eyebrow Color 3"),
        sem("Eyebrow Color Colour L2","Principled BSDF",12,0,"eyebrow_l2", "Eyebrow Color 4"),
        sem("Tongue Color Colour","Principled BSDF",3,0,"tongue","Tongue Color"),
        sem("Teeth Color Colour","Principled BSDF",4,0,"teeth","Teeth Color"),
        sem("Mouth Inside Color Colour","Principled BSDF",5,0,"mouth_inside","Mouth Inside Color"),
        sem("Cartoon Mouth Color Colour","Principled BSDF",15,0,"mouth_line","Cartoon Mouth Color"),
        sem("Emotion Line Color Colour","Principled BSDF",16,0,"emotion_line","Emotion Line Color"),

        #adv
        sep("Confirm Reset Ice Cube","confirm_ice_cube_reset","Confirm Ice Cube Reset"),

        #operators
        seo("Append Emotion Line","append.emotion"),
        seo("IK To FK Snapping Arm Right","fk_arm_r.snapping","R Arm IK > FK"),
        seo("IK To FK Snapping Arm Left","fk_arm_l.snapping","L Arm IK > FK"),
        seo("FK To IK Snapping Arm Right","fk_arm_r.snapping","R Arm FK > IK"),
        seo("FK To IK Snapping Arm Left","fk_arm_l.snapping","L Arm FK > IK"),
        seo("IK To FK Snapping Leg Right","fk_leg_r.snapping","R Leg IK > FK"),
        seo("IK To FK Snapping Leg Left","fk_leg_l.snapping","L Leg IK > FK"),
        seo("FK To IK Snapping Leg Right","fk_leg_r.snapping","R Leg FK > IK"),
        seo("FK To IK Snapping Leg Left","fk_arm_l.snapping","L Leg FK > IK"),
        seo("Updates Release New Ice Cube Download","ice_cube_check.updates","Check for Updates"),
        seo("Reset Ice Cube","reset_to_default.icecube","FULL ICE CUBE RESET"),

        #panel jumps

        # IC_Jump_To_Panel_RigStyle,
        # IC_Jump_To_Panel_MeshStyle,
        # IC_Jump_To_Panel_Controls,
        # IC_Jump_To_Panel_Skins,
        # IC_Jump_To_Panel_Eyes,
        # IC_Jump_To_Panel_Misc,
        # IC_Jump_To_Panel_DLC,
        # IC_Jump_To_Panel_Parenting,
        # IC_Jump_To_Panel_System,

        seo("Rig Bends Arm Type Fingers Face Mouth Rotate Eyelashes Tongue Dynamic Hair Teeth Bevels Jaw Depth Shortcut","jump_to_panel.rigstyle","Jump to Rig Style"),
        seo("Bulge Squish Deform Taper Hip Chest Boob Women Girl Body Eyebrow Shortcut","jump_to_panel.meshstyle","Jump to Mesh Style"),
        seo("Controls IK Inverse Kinematics Influence Bone Layers Snapping Lag Wireframe Tracker Global Shortcut","jump_to_panel.controls","Jump to Controls"),
        seo("Skin Username Auto SSS Radius SSS Color Colour Shortcut","jump_to_panel.skins","Jump to Skins"),
        seo("Eyes Eye Color Pupil Eye Colour Gradient Eye Texture Emission Roughness Specular Sparkle Size Bright Face Shortcut Iris","jump_to_panel.eyes","Jump to Eyes"),
        seo("Eyewhite Specular Roughness Eyebrow Tongue Teeth Mouth Inside Cartoon Emotion Line Shortcut","jump_to_panel.misc","Jump to Misc"),
        seo("Armor DLC Asset Download Rig Append Create Free Preset Generate Template Shortcut","jump_to_panel.dlc","Jump to DLC"),
        seo("Parenting Children Parent Connect Attach Follow Shortcut","jump_to_panel.parenting","Jump to Parenting"),
        seo("System Memory Lag Update Settings Data Export Import Backup Reload Reset Shortcut","jump_to_panel.system","Jump to System")
        )

    for entry in sorted(search_reference):
        if re.search(search_input,entry,re.IGNORECASE):
            if entry not in matching_props:
                matching_props.append(entry)

    for prop in matching_props:
        ref = search_reference[prop]
        ptype = ref['type']
        if ptype == 'prop':
            search_results_row = search_results_box.row(align=True)
            search_results_row.prop(data=ref['source'],property=str(ref['property_name']),text=str(ref['display_name']),expand=ref['expand'],toggle=ref['toggle'],index=ref['index'])
        elif ptype == 'op':
            search_results_row = search_results_box.row(align=True)
            search_results_row.operator(ref['op_name'],text=str(ref['display_name']))
        elif ptype == 'mat':
            try:
                material_list[ref['mat_id']].node_tree.nodes[ref['node']]

                search_results_row = search_results_box.row(align=True)
                search_results_row.prop(material_list[ref['mat_id']].node_tree.nodes[ref['node']].inputs[ref['input']], 'default_value', text=ref['display_name'],slider=True)
            except KeyError:
                pass

    if matching_props:
        pass
    else:
        search_results_row = search_results_box.row(align=True)
        search_results_row.label(text='No Results')

    matching_props.clear()

classes = [
]

def register():
    for cls in classes:
        bpy.utils.register_class(cls)

def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)

if __name__=="__main__":
    register()


import datetime
import json
import itertools
import os
import sys

from collections import defaultdict
from hashlib import md5
from time import sleep

import requests
from lxml.html import fromstring

CACHE_PATH = 'data/'

class BusTracker(object):
    api_url = 'http://ws.mybustracker.co.uk/'

    def __init__(self, api_key):
        self.api_key = api_key
        self._services = None

    @property
    def services(self):
        # Yeah, this is I/O on attribute access, don't do this
        if self._services is None:
            self._services = self.get_services()

        return self._services

    @property
    def key(self):
        time_string = datetime.datetime.utcnow().strftime("%Y%m%d%H")
        return md5(self.api_key + time_string).hexdigest()

    def get_all(self):
        routes = []
        for service in self.services:
            service_routes = self.get(service)
            self.services[service]['routes'] = service_routes
            routes.extend(service_routes)

        return routes

    def get(self, service):
        try:
            routes = self.get_routes(service)
        except ValueError:
            return []

        for route in routes:
            start, stop = get_route_endpoints(route)
            times = self.get_full_timetable(service, start['code'], stop['code'])

            route['buses'] = times

        return drop_duplicates(routes)

    def get_services(self):
        services = {}
        service_data = get_cached('getServices.json', self.request_services)
        for service in service_data['services']:
            services[service['mnemo']] = service

        return services

    def get_routes(self, service):
        return get_cached(
            "%s_route.json" % (service),
            self.request_routes,
            service
        )

    def get_full_timetable(self, service, start, stop):
        time = 0
        times = []
        errors = 0
        # Would you look at that, a while loop in python
        while (time < 2400):
            timetable = self.get_timetables(service, start, stop, "%04d" % time)
            if timetable:
                if (int(timetable[0]['start']) < time) and (errors < 3):
                    errors += 1
                    drop_cache("_".join([service, start, stop, "%04d" % time, 'timetable.html']))
                elif errors >= 3:
                    time += 200
                else:
                    errors = 0
                    times.extend(timetable)
                    time = int(timetable[-1]['start'])
            else:
                time += 200

        return times

    def get_timetables(self, *args):
        response = get_cached(
            "_".join(args + ("timetable.html",)),
            self.request_timetables,
            *args
        )

        doc = fromstring(response)
        rows = [
            elem.xpath('./td//text()') for elem in doc.xpath('//table[@class="timetable"][2]/tr')
        ]
        if not rows:
            return rows

        start_times, stop_times = rows[0][1:], rows[-1][1:]

        return [{"start": s, "stop": e} for s, e in zip(start_times, stop_times)]

    def request_services(self):
        return self.call_api('getServices')

    def request_routes(self, service):
        base_url = 'http://lothianbuses.com/tt/parse.php?service='
        return requests.get(base_url + service).json()

    def request_timetables(self, service, start, stop, time=None):
        base_url = 'http://lothianbuses.com/plan-a-journey/timetables/'

        return requests.get(base_url + service, params={
            's': start,
            'e': stop,
            'b': time,
            'sid': service,
            'p': 120,  # time range in minutes
            'd[]': 0,  # monday to friday
            'self': '',
        }).text

    def call_api(self, function, params=None):
        if params is None:
            params = {}
        else:
            params = params.copy()

        params.update({
            'module': 'json',
            'key': self.key,
            'function': function,
        })
        return requests.get(self.api_url, params=params).json()

# Cache utils

def is_cached(filename):
    return os.path.exists(CACHE_PATH + filename)

def save_data(data, filename):
    with open(CACHE_PATH + filename, 'w') as f:
        if filename.endswith('.json'):
            json.dump(data, f)
        else:
            f.write(data)

def read_data(filename):
    with open(CACHE_PATH + filename, 'r') as f:
        if filename.endswith('.json'):
            return json.load(f)
        else:
            return f.read()

def drop_cache(filename):
    os.remove(CACHE_PATH + filename)

def get_cached(filename, request_method, *args, **kwargs):
    cache_filename = filename
    if not is_cached(cache_filename):
        # plan-a-journey pages start returning wrong times if
        # there requests are coming without delay.
        # Also, it's good to be nice
        sleep(5)
        data = request_method(*args, **kwargs)
        save_data(data, cache_filename)
    else:
        data = read_data(cache_filename)

    return data

# Route utils

def drop_duplicates(routes):
    """Removes duplicate bus times from service routes.

    Some routes are a part of a larger service route, which means that
    some buses are added multiple times. In order to remove these duplicates
    we find the longest route for each start and stop time.

    """

    stop_routes = defaultdict(list), defaultdict(list)

    for index, kind in ((0, 'start'), (1, 'stop')):
        stop_routes = defaultdict(list)
        for route in routes:
            stop = get_route_endpoints(route)[index]
            stop_routes[stop['code']].append(route)

        for route_list in stop_routes.values():
            if len(route_list) <= 1:
                continue
            for r1, r2 in itertools.combinations(route_list, 2):
                if is_subroute(r1, r2):
                    parent, subroute = r2, r1
                elif is_subroute(r2, r1):
                    parent, subroute = r1, r2
                else:
                    continue
                subroute['buses'] = unique_times(subroute['buses'], parent['buses'], kind)

    return routes

def unique_times(subroute_buses, route_buses, time_key):
    """Removes entries from subroute_buses which exist in route_buses.

    Returns a new list with duplicates removed.
    Uses entry[time_key] for comparison.

    """

    route_times = set(bus[time_key] for bus in route_buses)
    return [bus for bus in subroute_buses if bus[time_key] not in route_times]

def get_route_endpoints(route):
    stops = get_route_stops(route)
    return stops[0], stops[-1]

def get_route_stops(route):
    return filter(lambda x: 'code' in x, route['points'])

def is_subroute(rA, rB):
    """Returns True if rA is a subroute of rB.

    Route A is considered a subroute if it's start and end stops are
    also stops of route B.

    """

    rA_stops = set(s['code'] for s in get_route_endpoints(rA))
    rB_stops = set(s['code'] for s in get_route_stops(rB))

    return rA_stops.issubset(rB_stops)

def compress_routes(routes):
    """Drops non-essential data to reduce route file size."""

    remaining_routes = []

    for route in routes:
        # Don't keep routes without any scheduled buses
        if not route['buses']:
            continue
        # Remove all points data except stop coordinates
        route['points'] = [{'lat': p['lat'], 'lng': p['lng']} for p in route['points']]

        remaining_routes.append(route)

    return remaining_routes

if __name__ == '__main__':
    api_key = sys.argv[1]

    bustracker = BusTracker(api_key=api_key)

    all_routes = bustracker.get_all()

    active_services = []
    for service in bustracker.services.values():
        service_routes = compress_routes(service['routes'])
        if service_routes:
            save_data(service_routes, '%s.json' % service['mnemo'])
            active_services.append({'name': service['name'], 'mnemo': service['mnemo']})

    active_services.sort(key=lambda x: int(x['mnemo']) if x['mnemo'].isdigit() else x['mnemo'])

    save_data(compress_routes(all_routes), 'all.json')
    save_data(active_services, 'services.json')