from django import template

register = template.Library()

@register.filter()
def smooth_timedelta(timedeltaobj):
    """Convert a datetime.timedelta object into Days, Hours, Minutes, Seconds."""
    secs = timedeltaobj.total_seconds()
    timetot = ""
    if secs > 86400: # 60sec * 60min * 24hrs
        days = secs // 86400
        timetot += "{} д.".format(int(days))
        secs = secs - days*86400

    if secs > 3600:
        hrs = secs // 3600
        timetot += " {} час.".format(int(hrs))
        secs = secs - hrs*3600

    if secs > 60:
        mins = secs // 60
        timetot += " {} мин.".format(int(mins))
        secs = secs - mins*60

    if secs > 0:
        timetot += " {} сек.".format(int(secs))
    return timetot


import pyttsx3
import PyPDF2

speaker = pyttsx3.init()
voices = speaker.getProperty('voices')
speaker.setProperty('voice', voices[1].id)
#rate = speaker.getProperty('rate')
speaker.setProperty("rate", 150)
speaker.say('i am alive I dont want it to go really slow just slow enough to be easy for a non native speaker to understand')

speaker.runAndWait()
#book  = open("","rb")


import numpy as np
import os
import torch
import torch.nn as nn
from torchvision import transforms
from torch.utils.data import Dataset, DataLoader
import math
from PIL import Image
from torchsummary import summary
from numpy import asarray

image_dir = "/content/Images"
SAVE_DIR = "/content/Results"
piece_dir = {}
batch_size = 128
image_size = (128, 128)  # Subject to change
epochs = 50

def popPeiceDir():
    for dir in os.listdir(image_dir):
        if dir.startswith('.'):
            continue
        piece_dir[dir] = os.path.join(image_dir, dir)

popPeiceDir()

# Empty Square = 0, Pawn = 1, King = 2, Queen = 3, Rook = 4, Bishop = 5, Knight = 6
def getFilesAndLabels():
    fl = []
    for key in piece_dir:
        if key == "Testimg" or key == "Testimg2":
            continue
        for file in os.listdir(piece_dir[key]):
            if file.startswith('.'):
                continue
            label = -1
            if key == "BB" or key == "WB":
                label = 5
            if key == "BK" or key == "WK":
                label = 6
            if key == "BKi" or key == "WKi":
                label = 2
            if key == "BP" or key == "WP":
                label = 1
            if key == "BQ" or key == "WQ":
                label = 3
            if key == "BR" or key == "WR":
                label = 4
            if key == "Empty":
                label = 0
            assert label != -1
            fl.append([os.path.join(piece_dir[key], file), label])
    return fl

def transformImg(img):
    preproc = transforms.Compose([transforms.Grayscale(),
                                  transforms.Resize(image_size), transforms.ToTensor(),
                                  transforms.Normalize((0.5), (0.5))])
    return preproc(img)

def getArr(image_path):
    img = Image.open(image_path)
    img = transformImg(img)
    return img

class DataSet(Dataset):
    def __init__(self, files_labels):
        self.fl = files_labels
        self.count = 0

    def __len__(self):
        return len(self.fl)

    def __getitem__(self, index):
        img_arr = getArr(self.fl[index][0])
        #label = torch.tensor([0, 0, 0, 0, 0, 0, 0])
        #label[self.fl[index][1]] = 1
        label = self.fl[index][1]
        return img_arr.float(), label

# Starting Image dims, Maxpooling Kernal, number of times maxpool is called in CNN, number of conv channels
def findFCFeatures(images_size, maxpooling, num_maxpool, num_channels):
    x, y = images_size
    mx, my = maxpooling
    for i in range(num_maxpool):
        x = math.floor(((x - (mx - 1) - 1) / mx) + 1)
        y = math.floor(((y - (my - 1) - 1) / my) + 1)
    return x * y * num_channels

def double_conv(in_channels, out_channels):
    return nn.Sequential(
        nn.Conv2d(in_channels, out_channels, 3, padding=1),
        nn.LeakyReLU(inplace=True),
        nn.Conv2d(out_channels, out_channels, 3, padding=1),
        nn.LeakyReLU(inplace=True))

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv_down1 = double_conv(1, 32)
        self.conv_down2 = double_conv(32, 64)
        self.conv_down3 = double_conv(64, 128)
        self.conv_down4 = double_conv(128, 256)
        self.maxpool = nn.MaxPool2d((2, 2))
        self.fc1 = nn.Linear(findFCFeatures(image_size, (2, 2), 3, 256), 7)

    def forward(self, x):
        conv1 = self.conv_down1(x)
        x = self.maxpool(conv1)
        conv2 = self.conv_down2(x)
        x = self.maxpool(conv2)
        conv3 = self.conv_down3(x)
        x = self.maxpool(conv3)
        x = self.conv_down4(x)
        x = x.view(-1, findFCFeatures(image_size, (2, 2), 3, 256))
        x = self.fc1(x)
        return x

def train(GPU=True):
    data = getFilesAndLabels()
    # print(data)
    dataset = DataSet(data)
    model = Net()
    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    if GPU:
        print(device)
        model.to(device)
    w = torch.tensor(np.array([1, 1, 1, 1, 1, 1, 1]))
    loss_func = nn.CrossEntropyLoss()

    tess = math.floor(len(data) * 0.2)
    trss = len(data) - tess

    train_subsamples, test_subsamples = torch.utils.data.random_split(range(len(data)), [trss, tess])

    train_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,
                                               num_workers=0, sampler=train_subsamples)
    test_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,
                                              num_workers=0, sampler=test_subsamples)

    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

    train_acc = []
    test_acc = []
    train_loss = []
    test_loss = []
    summary(model, (1, 128, 128))
    for epoch in range(epochs):
        model.train()
        train_num_correct = 0
        test_num_correct = 0
        for i, (inp, label) in enumerate(train_loader):
            if GPU:
                inp = inp.to(device)
                label = label.to(device)
            optimizer.zero_grad()
            cur_pre = model(inp)
            loss = loss_func(cur_pre, label.long())
            train_loss.append([loss, epoch])

            for i in range(cur_pre.shape[0]):
                if label[i] == torch.argmax(cur_pre[i]):
                    train_num_correct += 1
            # print('ML Started')
            loss.backward()
            optimizer.step()

        with torch.no_grad():
            for i, (inp, label) in enumerate(test_loader):
                if GPU:
                    inp = inp.to(device)
                    label = label.to(device)
                cur_pre = model(inp)
                loss = loss_func(cur_pre, label.long())
                test_loss.append([loss, epoch])

                for i in range(cur_pre.shape[0]):
                    if label[i] == torch.argmax(cur_pre[i]):
                        test_num_correct += 1

        train_acc.append(train_num_correct / trss)
        test_acc.append(test_num_correct / tess)
        print(f'Finished Epoch {epoch + 1}')
    print(f'Train Acc: {train_acc}')
    print(f'Test Acc: {test_acc}')
    print(f'Train Loss: {train_loss}')
    print(f'Test Loss: {test_loss}')
    train_acc = np.array(train_acc)
    test_acc = np.array(test_acc)
    torch.save(model.state_dict(), SAVE_DIR)
    np.savetxt(os.path.join(SAVE_DIR, 'trainAcc.csv'), train_acc, delimiter=',')
    np.savetxt(os.path.join(SAVE_DIR, 'testAcc.csv'), test_acc, delimiter=',')

train(GPU=True)


from setuptools import setup, find_packages

version = "0.1.7"

if __name__ == '__main__':
    setup(
        name="pygraph_utils",
        version=version,
        packages=find_packages(),
        python_requires='>=3.6',
        # include_package_data=True,
        # package_data={
        #     '': ['data/*.txt']
        # }
        url='https://github.com/microsoftgraph/dataconnect-solutions'
    )


import torch
from PIL import Image
from torchvision import models, transforms
from flask import Flask, request, jsonify
import pydicom

num_classes = 4
classes = ['COVID', 'Lung Opacity', 'Normal', 'Viral Pneumonia']
model = models.resnet18(pretrained=False)
model.fc = torch.nn.Linear(model.fc.in_features, num_classes)
model.load_state_dict(torch.load("pretrained_model.pth", map_location=torch.device('cpu')))
model.eval()

preprocess = transforms.Compose([
    transforms.Resize((200, 200)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

def classify_image(image):
    if image.mode != "RGB":
        image = image.convert("RGB")

    image = preprocess(image).unsqueeze(0)
    with torch.no_grad():
        outputs = model(image)
    _, predicted_class = torch.max(outputs, 1)
    return predicted_class.item()

def predict(image_data):
    if not image_data:
        return "No files selected", 400

    predictions = []

    for file in image_data:
        allowed_extensions = {"jpg", "jpeg", "png", "dcm"}

        if not file.filename.lower().endswith(tuple(allowed_extensions)):
            return f"Invalid file format: {file.filename}", 400

        if file.filename.lower().endswith(".dcm"):
            dcm = pydicom.dcmread(file)
            image = Image.fromarray(dcm.pixel_array)
        else:
            image = Image.open(file)

        class_id = classify_image(image)

        predictions.append({"filename": file.filename, "predicted_class": classes[class_id]})

    return jsonify(predictions)


"""Google Sign-In OAuth drop-in.

Google Sign-In API docs: https://developers.google.com/identity/protocols/OAuth2WebServer

Python API client docs: https://developers.google.com/api-client-library/python/

requests-oauthlib docs:

* https://requests-oauthlib.readthedocs.io/
* https://requests-oauthlib.readthedocs.io/en/latest/examples/google.html
"""
import logging

from flask import request
from google.cloud import ndb
from requests_oauthlib import OAuth2Session

from . import views, models
from .webutil import flask_util, util
from .webutil.util import json_dumps, json_loads

logger = logging.getLogger(__name__)

GOOGLE_CLIENT_ID = util.read('google_client_id')
GOOGLE_CLIENT_SECRET = util.read('google_client_secret')
AUTH_CODE_URL = 'https://accounts.google.com/o/oauth2/v2/auth'
ACCESS_TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token'
# Discovered on 1/30/2019 from:
#   https://accounts.google.com/.well-known/openid-configuration
# Background: https://developers.google.com/identity/protocols/OpenIDConnect#discovery
OPENID_CONNECT_USERINFO = 'https://openidconnect.googleapis.com/v1/userinfo'

class GoogleUser(models.BaseAuth):
  """An authenticated Google user.

  Provides methods that return information about this user and make OAuth-signed
  requests to Google APIs. Stores OAuth credentials in the datastore. See
  :class:`models.BaseAuth` for usage details.

  To make Google API calls: https://google-auth.readthedocs.io/
  """
  user_json = ndb.TextProperty()
  token_json = ndb.TextProperty()

  def site_name(self):
    return 'Google'

  def user_display_name(self):
    """Returns the user's name."""
    return json_loads(self.user_json).get('name') or 'unknown'

  def access_token(self):
    """Returns the OAuth access token string."""
    return json_loads(self.token_json)['access_token']

class Scopes(object):
  # OAuth/OpenID Connect scopes:
  #   https://developers.google.com/+/web/api/rest/oauth#authorization-scopes
  # Google scopes:
  #   https://developers.google.com/identity/protocols/googlescopes
  DEFAULT_SCOPE = 'openid https://www.googleapis.com/auth/userinfo.profile'
  SCOPE_SEPARATOR = ' '

class Start(Scopes, views.Start):
  """Starts the OAuth flow."""
  NAME = 'google_signin'
  LABEL = 'Google'
  """https://developers.google.com/accounts/docs/OAuth2WebServer#incrementalAuth"""
  INCLUDE_GRANTED_SCOPES = True

  def redirect_url(self, state=None):
    assert GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET, \
      "Please fill in the google_client_id and google_client_secret files in your app's root directory."

    session = OAuth2Session(GOOGLE_CLIENT_ID, scope=self.scope,
                            redirect_uri=self.to_url())
    auth_url, state = session.authorization_url(
      AUTH_CODE_URL, state=state,
      # ask for a refresh token so we can get an access token offline
      access_type='offline', prompt='consent',
      # https://developers.google.com/accounts/docs/OAuth2WebServer#incrementalAuth
      include_granted_scopes=self.INCLUDE_GRANTED_SCOPES)
    return auth_url

class Callback(Scopes, views.Callback):
  """Finishes the OAuth flow."""

  def dispatch_request(self):
    # handle errors
    state = request.values.get('state')
    error = request.values.get('error')
    desc = request.values.get('error_description')
    if error:
      msg = f'Error: {error}: {desc}'
      logger.info(msg)
      if error == 'access_denied':
        return self.finish(None, state=state)
      else:
        flask_util.error(msg)

    # extract auth code and request access token
    session = OAuth2Session(GOOGLE_CLIENT_ID, scope=self.scope,
                            redirect_uri=request.base_url)
    session.fetch_token(ACCESS_TOKEN_URL,
                        client_secret=GOOGLE_CLIENT_SECRET,
                        authorization_response=request.url)

    # get OpenID Connect user info
    # https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
    resp = session.get(OPENID_CONNECT_USERINFO)
    try:
      resp.raise_for_status()
    except BaseException as e:
      util.interpret_http_exception(e)
      raise

    user_json = json_loads(resp.text)
    logger.info('Got one person', user_json)

    user = GoogleUser(id=user_json['sub'], user_json=json_dumps(user_json),
                      token_json=json_dumps(session.token))
    user.put()
    return self.finish(user, state=state)


# -*- coding: utf-8 -*-
"""
Created on Mon Jan 18 21:17:44 2016

@author: zhicheng.qi
gramophone官网的爬虫，暂时爬feature这个栏目，只爬第一页
第一版
"""

import requests
from bs4 import BeautifulSoup
list_page = 'http://www.gramophone.co.uk/features/all'
r = requests.get(list_page)

soup = BeautifulSoup(r.text)

news_list = soup.findAll("div", {"class": "node-article-layout"})
for item in news_list:
    title = item.find('h2').get_text()
    link = item.find('h2').find('a')['href']
    link = list_page + link
    introduction = item.findAll('p', {'class': 'tuck-down'})[0].get_text()
    print('标题:', title)
    print('介绍:', introduction)
    print('链接:', link)
    print('\n')

# detail_page = 'http://www.gramophone.co.uk/feature/my-music-john-bercow'

# r = requests.get(detail_page)
# soup = BeautifulSoup(r.text)

# sub_heading = soup.find('h2',{'class' : 'sub-heading'}).get_text()


import json
import socket
import traceback
from threading import Thread

from network.protocolHandler import ProtocolHandler
from tools import logger

class Client:

    def __init__(self, ip: str, port: int):
        self.socket = socket.socket()
        self.connectState = False
        self.protocolHandle = ProtocolHandler()
        self.role = None

        thread = Thread(target=self.connect, args=(ip, port))
        thread.setDaemon(True)
        thread.start()

    def connect(self, ip: str, port: int):
        try:
            self.socket.connect((ip, port))
            self.send({"protocol": "cli_connect"})
            self.receiveMessage()
        except ConnectionRefusedError:
            logger.log("无法连接服务器")

    def receiveMessage(self):
        while True:
            data = self.socket.recv(4096)
            strPackage = data.decode("utf-8")
            packages = strPackage.split("|#|")
            print(packages)
            for jsonPackage in packages[:-1]:
                package = json.loads(jsonPackage)
                self.protocolHandle(self, package)

    def send(self, data: dict):
        self.socket.sendall((json.dumps(data, ensure_ascii=False) + "|#|").encode())


import pytest
from kubernetes.client import V2beta2CrossVersionObjectReference
from kubernetes.client import V2beta2ExternalMetricSource
from kubernetes.client import V2beta2ExternalMetricStatus
from kubernetes.client import V2beta2MetricIdentifier
from kubernetes.client import V2beta2MetricSpec
from kubernetes.client import V2beta2MetricStatus
from kubernetes.client import V2beta2MetricTarget
from kubernetes.client import V2beta2MetricValueStatus
from kubernetes.client import V2beta2ObjectMetricSource
from kubernetes.client import V2beta2PodsMetricSource
from kubernetes.client import V2beta2PodsMetricStatus
from kubernetes.client import V2beta2ResourceMetricSource
from kubernetes.client import V2beta2ResourceMetricStatus
from kubernetes.client.models.v2beta2_object_metric_status import (
    V2beta2ObjectMetricStatus,
)

from paasta_tools.instance.hpa_metrics_parser import HPAMetricsParser

@pytest.fixture
def parser():
    return HPAMetricsParser(hpa=None)

def test_parse_target_external_metric_value(parser):
    metric_spec = V2beta2MetricSpec(
        type="External",
        external=V2beta2ExternalMetricSource(
            metric=V2beta2MetricIdentifier(name="foo"),
            target=V2beta2MetricTarget(
                type="Value",
                average_value=12,
            ),
        ),
    )
    status = parser.parse_target(metric_spec)
    assert status["name"] == "foo"
    assert status["target_value"] == "12"

def test_parse_target_external_metric_average_value(parser):
    # The parser handles this case, but it's not currently
    # used in kubernetes_tools
    metric_spec = V2beta2MetricSpec(
        type="External",
        external=V2beta2ExternalMetricSource(
            metric=V2beta2MetricIdentifier(name="foo"),
            target=V2beta2MetricTarget(
                type="AverageValue",
                average_value=0.5,
            ),
        ),
    )
    status = parser.parse_target(metric_spec)
    assert status["name"] == "foo"
    assert status["target_value"] == "0.5"

def test_parse_target_pod_metric(parser):
    metric_spec = V2beta2MetricSpec(
        type="Pods",
        pods=V2beta2PodsMetricSource(
            metric=V2beta2MetricIdentifier(name="foo"),
            target=V2beta2MetricTarget(
                type="AverageValue",
                average_value=0.5,
            ),
        ),
    )
    status = parser.parse_target(metric_spec)
    assert status["name"] == "foo"
    assert status["target_value"] == "0.5"

def test_parse_target_resource_metric(parser):
    metric_spec = V2beta2MetricSpec(
        type="Resource",
        resource=V2beta2ResourceMetricSource(
            name="cpu",
            target=V2beta2MetricTarget(
                type="Utilization",
                average_utilization=0.5,
            ),
        ),
    )
    status = parser.parse_target(metric_spec)
    assert status["name"] == "cpu"
    assert status["target_value"] == "0.5"

def test_parse_target_object_metric(parser):
    metric_spec = V2beta2MetricSpec(
        type="Object",
        object=V2beta2ObjectMetricSource(
            metric=V2beta2MetricIdentifier(name="some-metric"),
            described_object=V2beta2CrossVersionObjectReference(
                api_version="apps/v1", kind="Deployment", name="deployment"
            ),
            target=V2beta2MetricTarget(
                type="Value",
                value=1,
            ),
        ),
    )
    status = parser.parse_target(metric_spec)
    assert status["name"] == "some-metric"
    assert status["target_value"] == "1"

def test_parse_current_external_metric_value(parser):
    metric_status = V2beta2MetricStatus(
        type="External",
        external=V2beta2ExternalMetricStatus(
            current=V2beta2MetricValueStatus(
                value=4,
            ),
            metric=V2beta2MetricIdentifier(name="foo"),
        ),
    )
    status = parser.parse_current(metric_status)
    assert status["name"] == "foo"
    assert status["current_value"] == "4"

def test_parse_current_external_metric_average_value(parser):
    # The parser handles this case, but it's not currently
    # used in kubernetes_tools
    metric_status = V2beta2MetricStatus(
        type="External",
        external=V2beta2ExternalMetricStatus(
            current=V2beta2MetricValueStatus(
                average_value=0.4,
            ),
            metric=V2beta2MetricIdentifier(name="foo"),
        ),
    )
    status = parser.parse_current(metric_status)
    assert status["name"] == "foo"
    assert status["current_value"] == "0.4"

def test_parse_current_pod_metric(parser):
    metric_status = V2beta2MetricStatus(
        type="Pods",
        pods=V2beta2PodsMetricStatus(
            current=V2beta2MetricValueStatus(
                average_value=0.4,
            ),
            metric=V2beta2MetricIdentifier(name="foo"),
        ),
    )
    status = parser.parse_current(metric_status)
    assert status["name"] == "foo"
    assert status["current_value"] == "0.4"

def test_parse_current_resource_metric(parser):
    metric_status = V2beta2MetricStatus(
        type="Resource",
        resource=V2beta2ResourceMetricStatus(
            current=V2beta2MetricValueStatus(
                average_utilization=0.4,
            ),
            name="cpu",
        ),
    )
    status = parser.parse_current(metric_status)
    assert status["name"] == "cpu"
    assert status["current_value"] == "0.4"

def test_parse_current_object_metric(parser):
    metric_status = V2beta2MetricStatus(
        type="Object",
        object=V2beta2ObjectMetricStatus(
            current=V2beta2MetricValueStatus(value=0.1),
            metric=V2beta2MetricIdentifier(name="some-metric"),
            described_object=V2beta2CrossVersionObjectReference(
                api_version="apps/v1", kind="Deployment", name="deployment"
            ),
        ),
    )

    status = parser.parse_current(metric_status)
    assert status["name"] == "some-metric"
    assert status["current_value"] == "0.1"

def test_parse_current_empty_string_metric(parser):
    metric_status = V2beta2MetricStatus(
        type="",
    )

    status = parser.parse_current(metric_status)
    assert status is None


__author__ = 'nncsang'
from magic import Magic
import magic
import sys
import os
import re
import mimetypes

def print_result(filename, real_ext, expected_ext):
    print("{:35s} \t {:10s} \t {:20s}".format(filename, real_ext, expected_ext))

# Default mime.types files
magic_file = "/etc/mime.types"
directory = None

# --------------------------------------------------------------------------------------------- #
# Check parameters
# --------------------------------------------------------------------------------------------- #
if (len(sys.argv) == 3):
    magic_file = sys.argv[2]
elif (len(sys.argv) == 2):
    pass
else:
    print("Usage: TypeDetector.py <directory> [magic_file]")
    exit(0)

directory = sys.argv[1]
type_maps = {}

# --------------------------------------------------------------------------------------------- #
# Parse mime.types
# --------------------------------------------------------------------------------------------- #
try:
    f = open(magic_file, "r")
    for line in f:
        if (len(line) == ""):
            continue

        if (line[0] == '#'):
            continue

        line = re.sub(r'\s+', '\t', line)

        parts = line.split("\t")

        type, ext = parts[0], parts[1:]
        type_maps[type] = ext
except:
    print("Cannot read " + magic_file)
    exit(0)

print("{:35s} \t {:10s} \t {:20s}".format('Camouflaged file', 'Claimed ext', 'Expected ext'))
# --------------------------------------------------------------------------------------------- #
# For each file:
#   + Get claimed ext
#   + Using magic and mime.types to find out expected exts
# --------------------------------------------------------------------------------------------- #

mg = Magic(mime=True, mime_encoding=False, keep_going=True, uncompress=False)
for folder, subs, files in os.walk(directory):
    for filename in files:
        try:
            full_path = folder + "//" + filename
            detected_type = str(mg.from_file(full_path).decode("utf-8"))
            ext = os.path.splitext(filename)[1][1:]
            expected_exts = type_maps[detected_type]
            if (ext != "" and ext not in expected_exts):
                # Print out highly likely camouflaged files
                print_result(full_path, ext, "".join([b + " " for b in expected_exts]))
        except:
            pass


# You are given a scratchcard consists of a row of non-negative integers.
# To win the scratch game, you need to scratch at least 2 numbers of the same value.
# You can only scratch consecutive numbers from the card. Scratching each number costs 1 dollar.
# Return the minimum cost to win the game, or -1 if winning is not possible.

from typing import List

def min_cost_to_win(nums: List[int]) -> int:
    possibleScratches = []

    for i in range(0, len(nums)):
        for j in range(i+1, len(nums)):
            if nums[i] == nums[j]:
                possibleScratches.append(nums[i:j+1])
            elif j == len(nums)-1:
                break

    print("possibleScratches: ", possibleScratches)

    while len(possibleScratches) > 1:
        if len(possibleScratches[0]) >= len(possibleScratches[1]):
            possibleScratches.remove(possibleScratches[0])
        elif len(possibleScratches[0]) < len(possibleScratches[1]):
            possibleScratches.remove(possibleScratches[1])

    return (len(possibleScratches[0]) if len(possibleScratches) !=0 else -1)

if __name__ == '__main__':
    nums = [1, 3, 4, 2, 3, 4, 5]
    res = min_cost_to_win(nums)
    print(res)


import json
import numpy as np
import os
import time
import torch
from pathlib import Path
from torch.utils.data import DataLoader
from tqdm import tqdm

import explaignn.heterogeneous_answering.graph_neural_network.dataset_gnn as dataset
from explaignn.heterogeneous_answering.graph_neural_network.gnn_factory import GNNFactory
from explaignn.heterogeneous_answering.heterogeneous_answering import HeterogeneousAnswering
from explaignn.library.utils import get_logger

torch.autograd.set_detect_anomaly(True)

START_DATE = time.strftime("%y-%m-%d_%H-%M", time.localtime())

class GNNModule(HeterogeneousAnswering):
    def __init__(self, config, iteration=None):
        super(GNNModule, self).__init__(config)
        self.logger = (
            get_logger(f"{__name__}_{iteration}", config)
            if iteration
            else get_logger(__name__, config)
        )

        self.gnn = GNNFactory.get_gnn(config)
        total_params = sum(p.numel() for p in self.gnn.parameters() if p.requires_grad)
        self.logger.info(f"Initialized model with {total_params} parameters.")
        self.model_loaded = False

    def train(self, sources=("kb", "text", "table", "info"), train_path=None, dev_path=None):
        """
        Train the model on generated HA data (skip instances for which answer is not there).
        Code inspired by: https://www.geeksforgeeks.org/training-neural-networks-with-validation-using-pytorch/.
        """
        self.logger.info(f"Loading data...")
        self.logger.info(f"Config: {json.dumps(self.config, indent=4)}")

        # set paths
        data_dir = self.config["path_to_intermediate_results"]
        qu = self.config["qu"]
        ers = self.config["ers"]
        sources_str = "_".join(sources)
        model_dir = os.path.dirname(self.config["gnn_model_path"])
        method_name = self.config["name"]

        # load parameters
        train_batch_size = self.config["gnn_train_batch_size"]
        eval_batch_size = self.config["gnn_eval_batch_size"]
        epochs = self.config["gnn_epochs"]
        lr = float(self.config["gnn_learning_rate"])
        weight_decay = float(self.config["gnn_weight_decay"])

        # load data
        if not train_path:
            train_path = os.path.join(
                data_dir, qu, ers, sources_str, f"train_ers-{method_name}.jsonl"
            )
        train_data = dataset.DatasetGNN(self.config, data_path=train_path, train=True)
        train_loader = DataLoader(
            train_data, batch_size=train_batch_size, shuffle=True, collate_fn=dataset.collate_fn
        )
        self.logger.info(f"Length train data: {len(train_data)}")

        if not dev_path:
            dev_path = os.path.join(data_dir, qu, ers, sources_str, f"dev_ers-{method_name}.jsonl")
        dev_data = dataset.DatasetGNN(self.config, data_path=dev_path, train=False)
        dev_loader = DataLoader(
            dev_data, batch_size=eval_batch_size, shuffle=True, collate_fn=dataset.collate_fn
        )
        self.logger.info(f"Length dev data: {len(dev_data)}")

        # initialize optimization
        optimizer = torch.optim.AdamW(self.gnn.parameters(), lr=lr, weight_decay=weight_decay)

        # start training loop
        min_dev_loss = np.inf
        min_dev_qa = -1
        steps = 0

        self.logger.info(f"Starting training...")

        for epoch in range(epochs):
            # training (for epoch)
            train_loss = 0.0
            self.gnn.train()
            for batch_index, instances in enumerate(tqdm(train_loader)):
                # move data to gpu (if possible)
                GNNModule._move_to_cuda(instances)
                # clear the gradients
                optimizer.zero_grad()
                # make prediction / forward pass
                output = self.gnn(instances, train=True)

                # update weights
                loss = output["loss"]
                loss.backward()
                torch.nn.utils.clip_grad_norm_(self.gnn.parameters(), self.config["gnn_clipping_max_norm"])
                optimizer.step()
                # calculate loss
                train_loss += loss.item()
                steps += 1
                self.logger.debug(f"Steps: {steps}, loss: {loss.item()}, aggregated loss: {train_loss}")

                # free up GPU space
                del instances

            # validation (for epoch)
            dev_loss = 0.0
            dev_qa_metrics = list()
            self.gnn.eval()
            with torch.no_grad():
                for batch_index, instances in enumerate(tqdm(dev_loader)):
                    # move data to gpu (if possible)
                    GNNModule._move_to_cuda(instances)
                    # make prediction / forward pass
                    output = self.gnn(instances, train=False)
                    # compute loss
                    loss = output["loss"]
                    # calculate loss
                    dev_loss += loss.item()
                    # aggregate QA metrics
                    qa_metrics = output["qa_metrics"]
                    dev_qa_metrics += qa_metrics

                    # free up GPU space
                    del instances

            # aggregate result
            avg_train_loss = train_loss / len(train_loader)
            avg_dev_loss = dev_loss / len(dev_loader)
            if dev_qa_metrics:
                avg_qa_metrics = {
                    key: f"{(sum([res[key] for res in dev_qa_metrics]) / len([res[key] for res in dev_qa_metrics])):.3f}"
                    for key in ["p_at_1", "mrr", "h_at_5", "answer_presence"]
                }
                avg_qa_metrics["num_questions"] = len(dev_qa_metrics)
            else:
                avg_qa_metrics = None

            # log results
            training_res = f"Epoch: {epoch+1}, Training loss: {avg_train_loss:.3f}."
            self.logger.info(training_res)
            dev_res = f"Epoch: {epoch+1}, Dev loss: {avg_dev_loss:.3f}."
            self.logger.info(dev_res)
            qa_metrics = f"Epoch: {epoch+1}, Dev QA metrics: {avg_qa_metrics}."
            self.logger.info(qa_metrics)
            decisive_metric = self.config["gnn_decisive_metric"]
            avg_dev_qa = float(avg_qa_metrics[decisive_metric])

            # save model if performance on dev improved
            if avg_dev_qa > min_dev_qa:
                self.logger.info(
                    f"""Saving the model! Dev performance ({decisive_metric}) increased ({min_dev_qa:.3f}--->{avg_dev_qa:.3f}).
                    Dev loss ({min_dev_loss:.3f}--->{avg_dev_loss:.3f})."""
                )
                min_dev_loss = avg_dev_loss
                min_dev_qa = avg_dev_qa

                # saving the model to standard path
                Path(model_dir).mkdir(parents=True, exist_ok=True)
                torch.save(self.gnn.state_dict(), self.config["gnn_model_path"])

                # saving the model (for not over-writing results)
                model_path = os.path.join(model_dir, f"model-{START_DATE}.bin")
                torch.save(self.gnn.state_dict(), model_path)

                # saving the config
                config_path = os.path.join(model_dir, f"config-{START_DATE}.json")
                with open(config_path, "w") as fp:
                    fp.write(json.dumps(self.config, indent=4))

                # saving the current result
                result_path = os.path.join(model_dir, f"result-{START_DATE}.res")
                with open(result_path, "w") as fp:
                    fp.write(f"{training_res}\n")
                    fp.write(f"{dev_res}\n")
                    fp.write(f"{qa_metrics}\n\n")

        # done
        self.logger.info(f"Finished training.")

    def inference_on_turns(self, turns, sources=("kb", "text", "table", "info"), train=False, use_tqdm=True):
        """Run inference on a multiple turns."""
        self.logger.info("Running inference_on_turns function!")
        self.load()

        if use_tqdm:
            p_bar = tqdm(total=len(turns))

        with torch.no_grad():
            batch_size = self.config["gnn_eval_batch_size"]

            # run inference
            instances = dataset.DatasetGNN.prepare_turns(self.config, turns, train=False)
            start_index = 0

            while start_index < len(instances):
                end_index = min(start_index + batch_size, len(instances))
                batch_instances = instances[start_index:end_index]
                batch = dataset.collate_fn(batch_instances)
                # move data to gpu (if possible)
                GNNModule._move_to_cuda(batch)
                output = self.gnn(batch, train=False)

                for i, _ in enumerate(batch_instances):
                    instance_index = start_index + i
                    turn = turns[instance_index]

                    # store
                    turn["ranked_answers"] = output["answer_predictions"][i]["ranked_answers"]

                    # add top-evidences within iterative GNN
                    if "gnn_max_output_evidences" in self.config:
                        top_evidences = output["evidence_predictions"][i]["top_evidences"]
                        turn["top_evidences"] = list(top_evidences)
                    else:
                        del turn["top_evidences"]

                    # obtain metrics
                    if "answers" in turn:  # e.g. for demo answers are not known
                        turn["p_at_1"] = output["qa_metrics"][i]["p_at_1"]
                        turn["mrr"] = output["qa_metrics"][i]["mrr"]
                        turn["h_at_5"] = output["qa_metrics"][i]["h_at_5"]
                        turn["answer_presence"] = output["qa_metrics"][i]["answer_presence"]

                    # delete other information
                    if "question_entities" in turn:
                        del turn["question_entities"]
                    if "silver_SR" in turn:
                        del turn["silver_SR"]
                    if "silver_relevant_turns" in turn:
                        del turn["silver_relevant_turns"]
                    if "silver_answering_evidences" in turn:
                        del turn["silver_answering_evidences"]
                    if "instance" in turn:
                        del turn["instance"]
                start_index += batch_size

                if use_tqdm:
                    p_bar.update(batch_size)
        return turns

    def inference_on_turn(self, turn, sources=("kb", "text", "table", "info"), train=False):
        """Run inference on a single turn."""
        return self.inference_on_turns([turn], sources, train, use_tqdm=False)[0]

    @staticmethod
    def _move_to_cuda(obj):
        if torch.cuda.is_available():
            for key, value in obj.items():
                if not type(obj[key]) is torch.Tensor:
                    continue
                obj[key] = obj[key].cuda()

    def load(self):
        """Load the model."""
        # only load if not already done so
        if not self.model_loaded:
            model_path = self.config["gnn_model_path"]
            if torch.cuda.is_available():
                state_dict = torch.load(model_path)
                if "encoder.encoder_linear.weight" in state_dict: del(state_dict["encoder.encoder_linear.weight"])
                if "encoder.encoder_linear.bias" in state_dict: del(state_dict["encoder.encoder_linear.bias"])
                self.gnn.load_state_dict(state_dict)
            else:
                state_dict = torch.load(model_path, map_location="cpu")
                if "encoder.encoder_linear.weight" in state_dict: del(state_dict["encoder.encoder_linear.weight"])
                if "encoder.encoder_linear.bias" in state_dict: del(state_dict["encoder.encoder_linear.bias"])
                self.gnn.load_state_dict(state_dict)
            self.gnn.eval()
            self.model_loaded = True


import time

import numpy as np
import torch

from metrics import MetricMeter, Writer
from sklearn.metrics import average_precision_score, roc_auc_score
from utils import get_eta, move_to, save_checkpoint, to_numpy

def train_loops(
    args, train_epoch_fn, model, optimizer, scheduler, loader_test, ema_model=None, best_mAP=0.
):
    writer = Writer(args.output_dir)
    total_epoch_time = 0.
    for epoch in range(args.start_epoch, args.num_epochs):
        start_time = time.time()
        print(f"Starting train epochs: {epoch + 1}/{args.num_epochs}")
        train_epoch_fn(args, model, optimizer, scheduler, ema_model=ema_model)

        if ((epoch + 1) % args.eval_periods == 0) or ((epoch + 1) == args.num_epochs):
            results = validate(args, loader_test, model)
            results.update({"epoch": epoch + 1})
            mAP = results["macro_aurpc"]

            # remember the best mAP and save the checkpoint
            is_best = mAP > best_mAP
            best_mAP = max(mAP, best_mAP)
            save_checkpoint(
                args.output_dir, {
                    'epoch': epoch + 1,
                    'model': args.model,
                    'state_dict': model.state_dict(),
                    'ema_model': ema_model.state_dict() if ema_model is not None else None,
                    'mAP': mAP,
                    'best_mAP': best_mAP,
                    'optimizer': optimizer.state_dict(),
                    'scheduler': scheduler.state_dict()
                }, is_best
            )

            # write metrics
            writer.write(results)

        # output eta
        total_epoch_time += (time.time() - start_time)
        avg_epoch_time = total_epoch_time / (epoch + 1)
        eta = get_eta(avg_epoch_time, epoch + 1, args.num_epochs)
        print(
            "[Epoch {}/{}]  Total elapsed: {:.1f} seconds, ETA: {}".format(
                epoch + 1, args.num_epochs, total_epoch_time, eta
            )
        )
        print()

    # end of epoch
    print(f"Best mAP: {best_mAP:.4f}, Last mAP: {mAP:.4f}")

def do_evaluate(all_predictions, all_labels):
    # mesaure test performances
    macro_aurpc = average_precision_score(all_labels, all_predictions, average="macro")
    micro_aurpc = average_precision_score(all_labels, all_predictions, average="micro")

    macro_auroc = roc_auc_score(all_labels, all_predictions, average="macro")
    micro_auroc = roc_auc_score(all_labels, all_predictions, average="micro")

    # print results
    print(
        "[Evaluation] {aurpc} | {auroc}".format(
            aurpc=f"macro AURPC: {macro_aurpc:.4f}, micro AURPC: {micro_aurpc:.4f}",
            auroc=f"macro AUROC: {macro_auroc:.4f}, micro AUROC: {micro_auroc:.4f}"
        )
    )
    results = {}
    results.update({"macro_aurpc": macro_aurpc, "micro_aurpc": micro_aurpc})
    results.update({"macro_auroc": macro_auroc, "micro_auroc": micro_auroc})
    return results

def validate(args, data_loader, model):
    model.eval()
    criterion = torch.nn.BCEWithLogitsLoss().cuda()
    metric_meters = MetricMeter()

    pred_list = []
    label_list = []
    print(f"Starting evaluation for {len(data_loader)} batches...")
    for i, (images, labels) in enumerate(data_loader):
        start_time = time.time()
        with torch.no_grad():
            if torch.cuda.is_available():
                images, labels = move_to((images, labels), "cuda")
            preds = model(images)
            loss = criterion(preds, labels.float())

            pred_list.append(to_numpy(preds))
            label_list.append(to_numpy(labels))

            # log some metrics
            metric_dict = {"loss": loss, "batch_time": time.time() - start_time}
            metric_meters.update(metric_dict)

        if ((i + 1) % 20 == 0) or ((i + 1) == len(data_loader)):
            _eta = get_eta(metric_meters.meters["batch_time"].avg, i + 1, len(data_loader))
            print(
                "{steps},  {eta},  {metrics}".format(
                    steps=f"Steps: {i+1}/{len(data_loader)}", eta=_eta, metrics=metric_meters
                )
            )

    pred_list = np.concatenate(pred_list, axis=0)
    label_list = np.concatenate(label_list, axis=0)
    results = do_evaluate(pred_list, label_list)
    results.update({"eval_loss": metric_meters.meters["loss"].avg})

    return results


# -*- coding: utf-8 -*-
__auth__ = 'zone'
__date__ = '2019/6/17 下午11:46'
'''
公众号：zone7
小程序：编程面试题库
'''
import os

print(os.getcwd())
import logging

from python.logging_model.code import sub_of_main

logger = logging.getLogger("zone7Model")
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler("log.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(formatter)

logger.addHandler(handler)
logger.addHandler(console)

sub = sub_of_main.SubOfMain()
logger.info("main module log")
sub.print_some_log()


import sys

from cx_Freeze import setup, Executable

build_exe_options = {
    "include_files": ["data"],
}

base = None
if sys.platform == "win32":
    base = "Win32GUI"

setup(
    name="Two Button Berserker",
    options={"build_exe": build_exe_options},
    executables=[Executable("main.py", base=base, targetName="TwoButtonBerserker")]
)


import pygame
from scripts.button import Button

class Action_Menu():
    def __init__(self, hand, game, gameboard, x, y):
        self.hand = hand
        self.game = game
        self.gameboard = gameboard
        self.isInit = False
        self.x, self.y = x + 75, y - 100 #offset away from the hand center
        self.original_x, self.original_y = x+75, y-100
        SCALE = .07
        #intialize button objects
        self.hit_button_image = pygame.image.load("images/buttons/wood_button_hit.png").convert_alpha()
        self.double_button_image = pygame.image.load("images/buttons/wood_button_double.png").convert_alpha()
        self.split_button_image = pygame.image.load("images/buttons/wood_button_split.png").convert_alpha()
        self.stand_button_image = pygame.image.load("images/buttons/wood_button_stand.png").convert_alpha()
        self.hit_button = Button(self.x, self.y, self.hit_button_image, scale=SCALE)
        self.double_button = Button(self.x, self.y, self.double_button_image, scale=SCALE)
        self.split_button = Button(self.x, self.y, self.split_button_image, scale=SCALE)
        self.stand_button = Button(self.x, self.y, self.stand_button_image, scale=SCALE)

    def reset_placement(self):
        self.x = self.original_x
        self.y = self.original_y
        self.isInit = False

    #This section of code allows Dynamic relocation of the player's possible playing options
    def init_dynamic_buttons(self):
        if self.isInit == False:

            if self.hand.hand_sum == 21:
                self.y -=35 #move the original placement down
            self.hit_button.rect.center = (self.x, self.y)

            self.y += 35 #places the next y coordinate
            #if hand can double down, update the button's next y coordinate
            if len(self.hand.card_list) <= 2 and self.hand.hand_sum <=11 and self.gameboard.player.fund > self.hand.bet_amount:
                self.double_button.rect.center = (self.x, self.y) #the next coor
                self.y += 35 #updates next y coordinate
            #if hand can split, update the button's next y coordinate
            if self.hand.card_list[0].type == self.hand.card_list[1].type and self.hand.hand_size == 2:
                self.split_button.rect.center = (self.x,self.y)
                self.y += 35 #updates next y coordinate
            #Update Stand's coordinates
            self.stand_button.rect.center = (self.x, self.y)
            self.isInit = True #run once

#TODO make the menu dynamicly relocate base on avaiable player options
    def display(self):
        #Allows Dynamic buttons to appear
        self.init_dynamic_buttons()

        #HIT HAND BUTTON
        if self.hand.hand_sum != 21:
            if self.hit_button.draw(self.game.display):
                self.gameboard.hit(self.hand)
                self.reset_placement() #resets for next available options

        #DOUBLE DOWN BUTTON
        #check if hand is doubleable, if the first 2 cards are less than 11
        if len(self.hand.card_list) <= 2 and self.hand.hand_sum <=11 and self.gameboard.player.fund > self.hand.bet_amount:
            if self.double_button.draw(self.game.display):
                #player hits 1 cards side ways (pygame image rotation)
                self.gameboard.deck_pile.top().image_surface= pygame.transform.rotate(surface=self.gameboard.deck_pile.top().image_surface, angle = 90)
                self.gameboard.hit(self.hand)
                #add more funds equal to current bet from player funds and subtract from player's fund
                self.gameboard.player.fund -= self.hand.bet_amount #subtract funds
                self.gameboard.player.current_bet += self.hand.bet_amount #update current round bet
                self.hand.bet_amount = self.hand.bet_amount*2 #update hand bet amount
                self.hand.stand = True#ends the hand's turn
        #SPLIT BUTTON
        #Check if hands has matching pairs
        if self.hand.card_list[0].type == self.hand.card_list[1].type and self.hand.hand_size == 2:
            if self.split_button.draw(self.game.display):
                #subtract funds and update UI
                self.gameboard.player.fund -= self.hand.bet_amount
                self.gameboard.player.current_bet += self.hand.bet_amount
                #create a new test hand next
                self.gameboard.player.split_hand(hand=self.hand,
                                               isExtra=True,
                                                bet_amount=self.hand.bet_amount,
                                                x=self.hand.x,
                                                y=self.hand.y,
                                                isDealer=False)
                #Moves the hand to the right
                self.hand.x += 60
                #reverts to the last known placement
                self.hand.placement.x = self.hand.x-10
                self.hand.placement.y = self.hand.y-20
                #shifts the card to the right
                for i , card in enumerate(self.hand.card_list):
                    card.rect.x += 70
                #Pass out new cards to both hands and continue to play
                current_index = self.gameboard.turn_list.index(self.hand)
                self.gameboard.hit(self.hand)
                self.gameboard.hit(self.gameboard.turn_list[current_index+1])

                self.original_x += 70
                self.reset_placement() #resets for next available options

        #STAND BUTTON
        if self.stand_button.draw(self.game.display):
            self.hand.stand = True


from dash import dash, dcc, html, Output, Input
import dash_bootstrap_components as dbc
import pandas as pd
import geopandas as gpd
import plotly.express as px
from dash_bootstrap_templates import load_figure_template
from pathlib import Path

# Load files
script_dir = Path(__file__).parent.absolute()
df = pd.read_csv(script_dir/'data/final/predicted_aqi.csv')
uhf34 = gpd.read_file(script_dir/'data/raw/UHF34.geo.json')
zone_data = {
    'UHF34 Zone': [101, 102, 103, 104, 105106107, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211,
                   301, 302, 303, 304, 305307, 306308, 309310, 401, 402, 403, 404406, 405, 407, 408, 409, 410, 501502, 503504],
    'Zone Name': ['Kingsbridge - Riverdale', 'Northeast Bronx', 'Fordham - Bronx Park', 'Pelham - Throgs Neck',
                  'South Bronx', 'Greenpoint', 'Downtown - Heights - Slope', 'Bedford Stuyvesant - Crown Heights',
                  'East New York', 'Sunset Park', 'Borough Park', 'East Flatbush - Flatbush', 'Canarsie - Flatlands',
                  'Bensonhurst - Bay Ridge', 'Coney Island - Sheepshead Bay', 'Williamsburg - Bushwick',
                  'Washington Heights - Inwood', 'Central Harlem - Morningside Heights', 'East Harlem',
                  'Upper West Side', 'Upper East Side - Gramercy', 'Chelsea - Village', 'Union Square, Lower Manhattan',
                  'Long Island City - Astoria', 'West Queens', 'Flushing - Clearview', 'Bayside - Meadows',
                  'Ridgewood - Forest Hills', 'Southwest Queen', 'Jamaica', 'Southeast Queens', 'Rockaway Queens',
                  'Northern Staten Island', 'Southern Staten Island']
}
zone_df = pd.DataFrame(zone_data)
df = pd.merge(df, zone_df, on='UHF34 Zone', how='left')

# Start Dash module with the MINTY theme, add templates for figures
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.MINTY])
load_figure_template(["minty", "minty_dark"])

#App format
app.layout = dbc.Container([
    # Type of air selector
    html.H1("NYC Air Quality Mapping", className="text-center mb-4"),
    dbc.Row([
        dbc.Col([
            html.Label("Select Air Type", className="mb-2"),
            dcc.Dropdown(
                id='name-dropdown',
                options=[
                    {'label': 'NO2', 'value': 'Nitrogen dioxide (NO2)'},
                    {'label': 'Fine Particles', 'value': 'Fine particles (PM 2.5)'},
                    {'label': 'Ozone', 'value': 'Ozone (O3)'},
                    {'label': 'AQI', 'value': 'AQI'},
                    {'label': 'Predicted AQI', 'value': 'Predicted AQI'}
                    ]
            )
        ], width=6),
        # Date Selector
        dbc.Col([
            html.Label("Select Date", className="mb-2"),
            dcc.DatePickerSingle(
                id='given_date',
                date='2009-06-08',  #Default
            )
        ], width=6),
    ]),
    # "Enter" button to activate map
    dbc.Row([
        dbc.Col(html.Button('Enter', id='submit-button', n_clicks=0, className="btn-lg"), width=12)
    ], className="mb-2"),
    # Map
    dbc.Row([
        dbc.Col(dcc.Graph(id='choropleth-map', style={'display': 'none'}), width=12)
    ])
], fluid=True, style={'display': 'flex', 'flex-direction': 'column', 'align-items': 'center'})

@app.callback(
    [Output('choropleth-map', 'figure'),
     Output('choropleth-map', 'style')],
    [Input('submit-button', 'n_clicks')],
    [Input('given_date', 'date'),
     Input('name-dropdown', 'value')]
)
def update_map(n_clicks, selected_time_period, selected_name):
    # Checks if button activated
    if n_clicks > 0 and selected_time_period and selected_name:
        filtered_df = df[df['Start_Date'] == selected_time_period]

        # Create the choropleth map
        fig = px.choropleth(
            filtered_df,
            geojson=uhf34,
            locations='UHF34 Zone',
            color=selected_name,
            featureidkey='properties.GEOCODE',
            hover_data={'UHF34 Zone': False, 'Zone Name': True},
            color_continuous_scale='mint',
            template='minty_dark'
        )
        # Center and resize the map to be in NYC
        fig.update_geos(
            center={'lat': 40.6828, 'lon': -73.9060},
            projection_scale=390,
            visible=False
        )
        fig.update_layout(
            height=725,  # Use the entire vertical height of the screen
            width=1400,
            margin=dict(l=0, r=0, t=20, b=0)  # Adjust margins to fill whole figure
        )
        return fig, {'display': 'block', 'height': '100vh'}
    else:
        return {}, {'display': 'none'}

if __name__ == '__main__':
    app.run_server(debug=True)


import copy
import json
import random
import igraph as ig

from itertools import chain

from intervention import (
    NetworkIntervention,
    IndividualIntervention,
    MockInterventionA,
    MockInterventionB,
)
from agent import Agent

class Simulation:
    def __init__(self, ticks, **kwargs):

        self.__dict__.update(kwargs)
        self.total_ticks = ticks
        self.cur_tick = 0
        self.history = {
            "agents": [],
            "edges": [],
            "vertices": [],
            "networks": [],
            "interventions": [],
            "parameters": [],
        }

    def setup(self):

        # Network
        self.network = ig.Graph.Erdos_Renyi(
            n=self.n_agents, p=self.p_edge, directed=False, loops=False
        )

        # Agents
        self.agents = []
        for i, v in enumerate(self.network.vs):
            agent = Agent(id=i, n_beh=self.n_beh)

            # Vertex and agent must have same name
            v["name"] = agent.name

            self.agents.append(agent)

        # Interventions
        self.interventions = []
        for intv_params in self.intervention_params:
            intv_class = globals()[intv_params["intv_class_name"]]
            intv = intv_class(**intv_params)
            self.interventions.append(intv)

        self.validate()

        self.record_history()

    def tick(self):

        self.validate()

        random.shuffle(self.agents)

        # (A) Conduct any interventions
        for intv in self.interventions:
            if intv.is_setup_phase(self.cur_tick):
                intv.setup(self.agents, self.network, sui_ORs=self.sui_ORs)

            if intv.is_active_phase(self.cur_tick):
                intv.intervene(self.agents, self.network)

        # (B) Agents interact with each other and world
        for i, agent in enumerate(self.agents):

            # (1) Emulate alters
            self.agents[i].emulate_alters(
                agents=self.agents, network=self.network, p=self.p_emul
            )

            # (2) Prune old, dissimilar alters
            self.agents[i].prune_alters(
                agents=self.agents, network=self.network, sim_thresh=self.sim_thresh
            )

            # (3) Recruit new, more similar alters
            self.agents[i].recruit_alters(
                agents=self.agents, network=self.network, sim_thresh=self.sim_thresh
            )

            # (4) Spontaneously change in favor of baserates
            self.agents[i].spontaneously_change(
                baserates=self.baserates, susceptibility=self.p_spon_change
            )

            # (5) Consider whether to attempt suicide
            self.agents[i].consider_suicide(
                odds_ratios=self.sui_ORs,
                gen_sui_prev=self.gen_sui_prev,
                gen_ave_beh=self.gen_ave_beh,
            )

        self.validate()

        self.record_history()

        self.cur_tick = self.cur_tick + 1

    def go(self):
        self.validate()

        while self.cur_tick < self.total_ticks:
            self.tick()

        self.validate()

    def validate(self):

        # No extra / missing agents or vertices
        agent_names = [a.name for a in self.agents]
        vertex_names = [v["name"] for v in self.network.vs]
        assert set(agent_names) == set(vertex_names)

        assert self.n_beh == len(self.agents[0].beh)
        assert self.n_beh == len(self.sui_ORs)
        assert self.n_beh == len(self.baserates)

        for intv in self.interventions:
            assert intv.start_tick <= self.total_ticks
            assert (intv.start_tick + intv.duration - 1) <= self.total_ticks

        return True

    def agents_to_dict(self):
        agent_dicts = [a.as_dict(self.agents, self.network) for a in self.agents]

        return agent_dicts

    def edges_to_dict(self):
        edges = []
        for edge in self.network.es:
            edges.append(
                {
                    "src_index": edge.source,
                    "src_name": edge.source_vertex["name"],
                    "tar_index": edge.target,
                    "tar_name": edge.target_vertex["name"],
                }
            )

        return edges

    def verts_to_dict(self):
        verts = []
        for vert in self.network.vs:
            verts.append({"index": vert.index, "name": vert["name"]})

        return verts

    def interventions_to_dict(self):
        intv_dicts = []
        for intv in self.interventions:
            intv_dict = intv.as_dict()
            intv_dicts.append(intv_dict)

        return intv_dicts

    def params_to_dict(self, flat=True, in_list=True):
        params = copy.deepcopy(self.__dict__)

        non_params = ["network", "agents", "interventions", "history"]
        params = {key: val for key, val in params.items() if key not in non_params}

        intv_params = params.pop("intervention_params")[0]

        if flat:
            listlike_params = ["tar_severity"]
            for listlike in listlike_params:
                for i, item in enumerate(intv_params.pop(listlike)):
                    intv_params.update({f"{listlike}{i}": item})

            listlike_params = ["sui_ORs", "baserates"]
            for listlike in listlike_params:
                for i, item in enumerate(params.pop(listlike)):
                    params.update({f"{listlike}{i}": item})

        params.update(intv_params)

        if in_list:
            params = [params]

        return params

    def network_to_dict(self, in_list=True):
        d = {}

        d.update(
            {
                "density": self.network.density(),
                "n_agents": len(self.network.vs),
                "n_edges": len(self.network.es),
                "n_isolates": len([v for v in self.network.vs if v.degree() == 0]),
            }
        )

        # individual assortativities
        for i in range(self.n_beh):
            agent_behs = [a.beh[i] for a in self.agents]
            assort = self.network.assortativity(types1=agent_behs, directed=False)
            d.update({f"assort_beh{i}": assort})

        beh_sums = [sum(a.beh) for a in self.agents]
        d["assort_sum_beh"] = self.network.assortativity(
            types1=beh_sums, directed=False
        )

        cur_risks = [a.current_risk for a in self.agents]
        d["assort_cur_risk"] = self.network.assortativity(
            types1=cur_risks, directed=False
        )

        if in_list:
            d = [d]

        return d

    # def record_history(self):

    #     self.history["agents"].append(copy.deepcopy(self.agents_to_dict()))
    #     self.history["edges"].append(copy.deepcopy(self.edges_to_dict()))
    #     self.history["vertices"].append(copy.deepcopy(self.verts_to_dict()))
    #     self.history["networks"].append(copy.deepcopy(self.network_to_dict()))
    #     self.history["interventions"].append(
    #         copy.deepcopy(self.interventions_to_dict())
    #     )
    #     self.history["parameters"].append(copy.deepcopy(self.params_to_dict()))

    def record_history(self):

        self.history["agents"].append(self.agents_to_dict())
        self.history["edges"].append(self.edges_to_dict())
        # self.history["vertices"].append(self.verts_to_dict())
        self.history["networks"].append(self.network_to_dict())
        self.history["interventions"].append(self.interventions_to_dict())

        if self.cur_tick <= 1:
            self.history["parameters"].append(self.params_to_dict())

    def tag_history(self):

        # Go through each kind of history. Within each kind of history,
        # tag all of the individual items with the correct tick and with the
        # sim_id for this simulation, which can be used to uniquely identify it

        for aspect in self.history:
            for i, objs in enumerate(self.history[aspect]):
                for obj in objs:
                    obj.update({"tick": i, "sim_id": self.sim_id})

    def history_to_db(self, con, history_of=None, simplify=True):
        if history_of is None:
            history_aspects = ["agents", "edges", "parameters"]
        else:
            history_aspects = history_of

        for history_aspect in history_aspects:
            history = self.history_to_dataframe(history_aspect)
            # history["seed"] = self.seed
            history["sample_num"] = self.sample_num

            if simplify and history_aspect == "parameters":
                history = history.iloc[[0]]

            history.to_sql(history_aspect, con, if_exists="append", index=False)

    def history_for_db(self, simplify=True):
        self.tag_history()

        exportable_history = {}
        for history_of in [
            "agents",
            "edges",
            "parameters",
            "networks",
            "interventions",
        ]:
            flat_history = list(chain.from_iterable(self.history[history_of]))
            exportable_history[history_of] = flat_history

        if simplify:
            exportable_history["parameters"] = [exportable_history["parameters"][0]]

        return exportable_history

    @staticmethod
    def db_var_types(dict_list):
        db_mapping = {
            "<class 'int'>": "INTEGER",
            "<class 'numpy.int64'>": "INTEGER",
            "<class 'str'>": "TEXT",
            "<class 'bool'>": "INTEGER",
            "<class 'float'>": "REAL",
            "<class 'numpy.float64'>": "REAL",
        }

        var_types = {}
        for key, val in dict_list.items():
            var_type = db_mapping[str(type(val))]
            var_types.update({key: var_type})

        return var_types

    def create_history_tables(self, con):
        history = self.history_for_db()

        for aspect, data in history.items():
            var_types = ", ".join(Simulation.db_var_types(data[-1]))
            query = f"CREATE TABLE IF NOT EXISTS {aspect}({var_types})"

            with con:
                con.execute(query)

    def insert_history_to_db(self, con):
        history = self.history_for_db()

        for aspect, data in history.items():
            colnames = ", ".join([f":{key}" for key in data[-1]])
            query = f"INSERT INTO {aspect} VALUES({colnames})"

            with con:
                con.executemany(query, data)

if __name__ == "__main__":
    from main import run_simulation

    x = run_simulation(1)


from tkinter import *
import tkinter.messagebox
import sqlite3
import os.path
import sys
sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/' + '../..'))
from HospitalManagementDBMS import settings

settings.init()

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.join(BASE_DIR, "../database.db")
conn = sqlite3.connect(db_path)
c = conn.cursor()

class Application:
    def __init__(self, master):
        self.master = master
        self.heading = Label(master, text="Update Appointments",  fg='steelblue',bg='lightgreen', font=('arial 40 bold'))
        self.heading.place(x=150, y=0)

        self.name = Label(master, text="Enter Patient's Name",bg='lightgreen', font=('arial 18 bold'))
        self.name.place(x=0, y=60)

        self.namenet = Entry(master, width=30)
        self.namenet.place(x=280, y=62)

        self.search = Button(master, text="Search", width=12, height=1, bg='steelblue', command=self.search_db)
        self.search.place(x=350, y=102)

    def search_db(self):
        self.input = self.namenet.get()

        sql = "SELECT * FROM appointments WHERE name LIKE ?"
        self.res = c.execute(sql, (self.input,))
        for self.row in self.res:
                self.name1 = self.row[1]
                self.age = self.row[2]
                self.gender = self.row[3]
                self.location = self.row[4]
                self.time = self.row[6]
                self.phone = self.row[5]
                self.reason = self.row[7]

        self.uname = Label(self.master, text="Patient's Name",bg='lightgreen', font=('arial 18 bold'))
        self.uname.place(x=0, y=140)

        self.uage = Label(self.master, text="Age",bg='lightgreen', font=('arial 18 bold'))
        self.uage.place(x=0, y=180)

        self.ugender = Label(self.master, text="Gender",bg='lightgreen', font=('arial 18 bold'))
        self.ugender.place(x=0, y=220)

        self.ulocation = Label(self.master, text="Location",bg='lightgreen', font=('arial 18 bold'))
        self.ulocation.place(x=0, y=260)

        self.uphone = Label(self.master, text="Phone Number",bg='lightgreen', font=('arial 18 bold'))
        self.uphone.place(x=0, y=300)

        self.utime = Label(self.master, text="Appointment Time",bg='lightgreen', font=('arial 18 bold'))
        self.utime.place(x=0, y=340)

        self.ureason = Label(self.master, text="Reason", font=('arial 18 bold'), fg='black', bg='lightgreen')
        self.ureason.place(x=0, y=380)

        self.ent1 = Entry(self.master, width=30)
        self.ent1.place(x=300, y=140)
        self.ent1.insert(END, str(self.name1))

        self.ent2 = Entry(self.master, width=30)
        self.ent2.place(x=300, y=180)
        self.ent2.insert(END, str(self.age))

        self.variable1 = StringVar(self.master)
        self.variable1.set(settings.genders[settings.genders.index(self.gender)])
        self.ent3 = OptionMenu(self.master, self.variable1,*settings.genders)
        self.ent3.place(x=300, y=220)

        self.ent4 = Entry(self.master, width=30)
        self.ent4.place(x=300, y=260)
        self.ent4.insert(END, str(self.location))

        self.ent5 = Entry(self.master, width=30)
        self.ent5.place(x=300, y=300)
        self.ent5.insert(END, str(self.phone))

        self.ent6 = Entry(self.master, width=30)
        self.ent6.place(x=300, y=340)
        self.ent6.insert(END, str(self.time))

        self.variable = StringVar(self.master)
        self.variable.set(settings.diseases[settings.diseases.index(self.reason)])
        self.ent7 = OptionMenu(self.master, self.variable,*settings.diseases)
        self.ent7.place(x=300, y=380)

        self.update = Button(self.master, text="Update", width=20, height=2, bg='lightblue', command=self.update_db)
        self.update.place(x=400, y=420)

        self.delete = Button(self.master, text="Delete", width=20, height=2, bg='red', command=self.delete_db)
        self.delete.place(x=150, y=420)

    def update_db(self):
                self.var1 = self.ent1.get()
                self.var2 = self.ent2.get()
                self.var3 = self.variable1.get()
                self.var4 = self.ent4.get()
                self.var5 = self.ent5.get()
                self.var6 = self.ent6.get()
                self.var7 = self.variable.get()

                if self.var1 == '' or self.var2 == '' or self.var4 == '' or self.var5 == '' or self.var6 == '':
                    tkinter.messagebox.showinfo("Warning", "Please Fill Up All Boxes")
                elif int(self.var2)<0 or int(self.var2)>150 or not self.var4.isalpha() or not self.var5.isdigit():
                    tkinter.messagebox.showinfo("Warning", "Invalid input")
                elif self.var6 in settings.times1:
                    tkinter.messagebox.showinfo("Warning", "Appointment at this time alreadry exits\n")
                else:
                    query = "UPDATE appointments SET name=?, age=?, gender=?, location=?,phone=?, scheduled_time=?, for=? WHERE name LIKE ?"
                    c.execute(query, (self.var1, self.var2, self.var3, self.var4, self.var5, self.var6, self.var7, self.namenet.get(),))
                    conn.commit()
                    tkinter.messagebox.showinfo("Updated", "Successfully Updated.")
    def delete_db(self):
                sql2 = "DELETE FROM appointments WHERE name LIKE ?"
                sql3 = "UPDATE sqlite_sequence set seq=seq-2 WHERE name='appointments' and seq>0"
                sql5 = "SELECT count(id) FROM appointments"
                sql6 = "UPDATE sqlite_sequence set seq=0 where name='appointments"

                c.execute(sql3)
                c.execute(sql2, (self.namenet.get(),))
        #if(c.execute(sql5)==0):
        #    c.execute(sql6)
                conn.commit()
                tkinter.messagebox.showinfo("Success", "Deleted Successfully")
                self.uname.destroy()
                self.uage.destroy()
                self.ugender.destroy()
                self.ulocation.destroy()
                self.uphone.destroy()
                self.utime.destroy()
                self.ureason.destroy()
                self.ent1.destroy()
                self.ent2.destroy()
                self.ent3.destroy()
                self.ent4.destroy()
                self.ent5.destroy()
                self.ent6.destroy()
                self.ent7.destroy()
                self.update.destroy()
                self.delete.destroy()
                self.namenet.delete(0,'end')

root = Tk()
b = Application(root)
root.geometry("1200x720+0+0")
root.title("Update Appointments")
root.resizable(False, False)
root.config(bg='lightgreen')
root.mainloop()


'''
Several Models used for testing.
'''

import numpy as np
from scipy.sparse.csgraph import minimum_spanning_tree

from .blocks import get_demo_circuit, get_demo_circuit_v2
from .dataset import gaussian_pdf, barstripe_pdf, barstripe_wf
from .qcbm import QCBM
from .cloneqcbm import CloneQCBM, CloneGAN
from .mmd import RBFMMD2
from .structure import chowliu_tree, nearest_neighbor, random_tree
from .contexts import ProjectQContext

def load_gaussian(num_bit, depth, context='scipy'):
    '''gaussian distribution.'''
    geometry = (num_bit,)
    hndim = 2**num_bit

    # standard circuit
    pairs = nearest_neighbor(geometry)
    circuit = get_demo_circuit(num_bit, depth, pairs)

    # bar and stripe
    p_bs = gaussian_pdf(geometry, mu=hndim/2., sigma=hndim/4.)

    # mmd loss
    mmd = RBFMMD2([0.25,4], num_bit, False)

    # Born Machine
    bm = QCBM(circuit, mmd, p_bs)
    if context == 'projectq':
        bm.context = ProjectQContext
    return bm

def load_barstripe(geometry, depth, context='scipy', structure='nn'):
    '''3 x 3 bar and stripes.'''
    num_bit = np.prod(geometry)

    # bar and stripe
    p_bs = barstripe_pdf(geometry)

    # standard circuit
    if structure == 'random-tree':
        pairs = random_tree()
    elif structure == 'chowliu-tree':
        pairs = chowliu_tree(p_bs)
    elif structure == 'nn':
        # nearest neighbor
        pairs = nearest_neighbor(geometry)
    else:
        raise ValueError('unknown entangle structure %s!'%structure)
    circuit = get_demo_circuit(num_bit, depth, pairs)

    # mmd loss
    mmd = RBFMMD2([0.5], num_bit, True)

    # Born Machine
    bm = QCBM(circuit, mmd, p_bs)
    if context == 'projectq':
        bm.context = ProjectQContext
    return bm

def load_complex(geometry, depth, batch_size=None, version='scipy'):
    num_bit = np.prod(geometry)

    # standard circuit
    pairs = nearest_neighbor(geometry)
    circuit = get_demo_circuit_v2(num_bit, depth, pairs, trim_tail=False)

    # complex wave function
    theta_list = np.random.random(circuit.num_param)*2*np.pi
    wf = np.zeros(2**num_bit, dtype='complex128')
    wf[0] = 1
    circuit(wf, theta_list)
    #print(wf)
    wf = barstripe_wf(geometry)

    # mmd loss
    mmd = RBFMMD2([1], num_bit, True)

    # Born Machine
    bm = CloneQCBM(circuit, mmd, wf, batch_size=batch_size)
    if version == 'projectq':
        bm.context = ProjectQContext
    return bm

def load_complexgan(geometry, depth, batch_size=None, version='scipy'):
    num_bit = np.prod(geometry)

    # standard circuit
    pairs = nearest_neighbor(geometry)
    circuit = get_demo_circuit(num_bit, depth, pairs, trim_tail=False)

    # complex wave function
    wf = barstripe_wf(geometry)

    # mmd loss
    mmd = RBFMMD2([1], num_bit, True)

    # Born Machine
    bm = CloneGAN(circuit, mmd, wf, batch_size=batch_size)
    if version == 'projectq':
        bm.context = ProjectQContext
    return bm


import math
from itertools import zip_longest
from typing import Type, Union, Set, List, Tuple
import networkx as nx
from networkx.classes.filters import no_filter

from weaveio.hierarchy import Multiple, Hierarchy, OneOf, Graphable

def normalise_relation(h):
    if not isinstance(h, Multiple):
        h = OneOf(h)
    h.instantate_node()
    relation, node = h, h.node
    return relation, node

def get_all_subclasses(cls: Type) -> List[Type]:
    all_subclasses = []
    for subclass in cls.__subclasses__():
        all_subclasses.append(subclass)
        all_subclasses.extend(get_all_subclasses(subclass))
    return all_subclasses

def collapse_classes_to_superclasses(graph, classes):
    """
    Given a list of classes, return the shortest list of classes possible by merging classes together.
    A merge is only possible if they share a base class
    class B(A) and class C(A) can be merged -> class A
    In graph terms, this is equivalent to finding islands (connected components) and returning
     the shallowest node that has a path to all leaves (the head(s))
    """
    G = nx.subgraph_view(graph, filter_node=lambda n: n != Hierarchy and n in classes,
                         filter_edge=lambda *e: graph.edges[e]['type'] == 'subclassed_by')
    for ns in nx.connected_components(G.to_undirected()):
        subgraph = nx.subgraph(G, ns)
        for n in subgraph.nodes:
            if not subgraph.in_degree(n):
                yield n

def hierarchies_from_hierarchy(hier: Type[Hierarchy], done=None, templates=False) -> Set[Type[Hierarchy]]:
    if done is None:
        done = []
    hierarchies = set()
    todo = {h.node if isinstance(h, Multiple) else h for h in hier.parents + hier.children + hier.produces}
    if not templates:
        todo = {h for h in todo if not h.is_template}
    else:
        todo.update({h for h in todo for hh in get_all_class_bases(h) if issubclass(hh, Hierarchy)})
    for new in todo:
        if isinstance(new, Multiple):
            new.instantate_node()
            h = new.node
        else:
            h = new
        if h not in done and h is not hier:
            hierarchies.update(hierarchies_from_hierarchy(h, done, templates))
            done.append(h)
    hierarchies.add(hier)
    return hierarchies

def expand_template_relation(relation):
    """
    Returns a list of relations that relate to each non-template class
    e.g.
    >>> expand_template_relation(Multiple(L1StackedSpectrum))
    [Multiple(L1SingleSpectrum), Multiple(L1StackSpectrum), Multiple(L1SuperstackSpectrum)]
    """
    if not relation.node.is_template:
        return [relation]
    subclasses = [cls for cls in get_all_subclasses(relation.node) if not cls.is_template and issubclass(cls, Hierarchy)]
    return [Multiple(subclass, 0, relation.maxnumber, relation.constrain, relation.relation_idname, relation.one2one) for subclass in subclasses]

def shortest_simple_paths(G, source, target, weight):
    try:
        return nx.shortest_simple_paths(G, source, target, weight)
    except nx.NetworkXNoPath:
        return None

def pick_shortest_multiedge(G, a, b, weight=None):
    edges = set(G.out_edges(a, keys=True)) & set(G.in_edges(b, keys=True))
    if not edges:
        raise nx.NetworkXNoPath("No path found between %s and %s" % (a, b))
    if weight is not None:
        return min(edges, key=lambda e: G.edges[e][weight])
    return edges.pop()

def edges_from_path(G, path, weight=None):
    return list(map(lambda e: pick_shortest_multiedge(G, *e, weight=weight), zip(path[:-1], path[1:])))

def find_forking_path(graph, top, bottom, weight=None):
    done = set()
    parents = nx.subgraph_view(graph, filter_edge=lambda *e: graph.edges[e]['type'] != 'is_a')
    is_a_graph = nx.subgraph_view(graph, filter_edge=lambda *e: graph.edges[e]['type'] == 'is_a')
    gen = nx.all_shortest_paths(graph, bottom, top, weight)
    try:
        todo = [next(gen)]
    except nx.NetworkXNoPath:
        return []
    while todo:
        _path = todo.pop()
        edges = edges_from_path(graph, _path, weight)
        types = [graph.edges[edge]['type'] for edge in edges]
        path = [_path[0]]
        for i, (edge, typ) in enumerate(zip(edges, types)):
            if typ == 'is_a' and edge[1] != top:
                # try to find a path using the superclass
                subpaths = find_forking_path(parents, top, edge[0], weight)
                if subpaths:
                    for subpath in subpaths:
                        done.add((*path[:-1], *subpath))
                else:  # otherwise expand all classes
                    subclasses = [i for i in get_all_subclasses(edge[0]) if not i.is_template]
                    if top in subclasses:
                        inheritance_path = nx.shortest_path(is_a_graph, edge[0], top)
                        done.add((*path, *inheritance_path[1:]))
                    else:
                        for subclass in subclasses:
                            inheritance_path = nx.shortest_path(is_a_graph, edge[0], subclass)
                            for subpath in find_forking_path(graph, top, subclass, weight):
                                done.add((*path, *inheritance_path[1:-1], *subpath))
                break
            else:
                path.append(edge[1])
        else:  # only append here, if we've cycled through all edges without breaking
            done.add(tuple(path))
    return done

def find_paths(graph, a, b, weight='weight') -> Set[Tuple[Type[Hierarchy]]]:
    """
    Returns a set of paths from a to b
    Not all paths are guaranteed to be valid, for example:
        l2superstack.ob yields paths of which `L2superstack-...-L1Single-...-ob` is one.
        This path is invalid, but it is included anyway because it is one of a set
        (through single,stack,superstack,supertarget).
        When queried, invalid paths will yield 0 results so it is not a problem.
    """
    *sorted_nodes, G, use_children = graph.sort_deepest(a, b)
    G = G.reverse()
    _paths = find_forking_path(G, *sorted_nodes, weight)
    _paths = {(p[0], p[0]) if len(p) == 1 else p for p in _paths}
    # put in requested order (a-->b)
    # now remove chains of is_a
    # so x-l1spectrum-l1stacked-l1stack becomes x-l1stack
    reduced_paths = set()
    for ip, path in enumerate(_paths):
        edges = edges_from_path(G, path, weight)
        reduced_path = [edge[0] for edge in edges if G.edges[edge]['type'] not in ['is_a', 'subclassed_by']]
        reduced_path = (*reduced_path, path[-1])
        if not use_children:
            reduced_path = reduced_path[::-1]  # reverse it because the actual database has only parent relations
            edges = (graph.short_edge(e[1], e[0], weight) for e in edges[::-1])
        reduced_paths.add((reduced_path, tuple(edges)))
    return reduced_paths

class HierarchyGraph(nx.MultiDiGraph):
    """
    A multidigraph where edges have keys
    """
    def initialise(self):
        hiers = get_all_subclasses(Hierarchy)
        for h in hiers:
            self._add_hierarchy(h)
        self.remove_node(Hierarchy)
        self._assign_edge_weights()

    def to_neo4j(self, graph):
        node_type = 'AbstractNode'
        for node in self.nodes:
            graph.execute(f"""MERGE (n:{node.__name__}{node_type}:{node_type}) ON CREATE
            SET n.factors = $factors, n.identifier_builder = $identifier_builder, n.singular_name=$singular_name,
             n.plural_name = $plural_name, n.idname = $idname, n.products = $products""",
                          factors=node.factors, singular_name=node.singular_name, plural_name=node.plural_name,
                          idname=node.idname, identifier_builder=node.identifier_builder, products=node.products)
        for edge in self.edges:
            rel = {k: v for k, v in self.edges[edge].items() if not isinstance(v, Multiple)}
            try:
                rel.update(self.edges[edge]['relation'].to_reldict())
            except KeyError:
                rel['singular'] = False
            if 'constrain' in rel:
                rel['constrain'] = [i.__name__ for i in rel['constrain']]
            graph.execute(f"""MATCH (a:{edge[0].__name__}{node_type})
            MATCH (b:{edge[1].__name__}{node_type})
            MERGE (a)-[r:{rel.pop('type')}]->(b)
            ON CREATE SET r = $rel
            """, rel=rel)

    def add_edge(self, u_for_edge, v_for_edge, idname=None, **attr):
        """
        Create an edge if the edge (uniquely identified by attributes) doesn't exist
        the edge key will be an unused integer or the idname of the relation
        if idname is given, it will be used and attr is not checked for uniqueness (only ever 1 edge)
        if idname is not given, attr is checked for uniqueness and key is the number of the same edges
        """
        if not attr:
            return super(HierarchyGraph, self).add_edge(u_for_edge, v_for_edge, idname)
        hsh = hash(tuple(attr.items()))
        if idname is not None:
            # use idname as unique key
            key = super(HierarchyGraph, self).add_edge(u_for_edge, v_for_edge, idname, hsh=hsh, **attr)
            # and make a "general" link as well
            if (u_for_edge, v_for_edge, '*') in self.edges:
                self.edges[u_for_edge, v_for_edge, '*']['singular'] = False
                self.edges[u_for_edge, v_for_edge, '*']['actual_number'] += attr['actual_number']
            else:
                super(HierarchyGraph, self).add_edge(u_for_edge, v_for_edge, '*', hsh=hsh, **attr)
            return key
        if u_for_edge in self and v_for_edge in self:
            edges = set(self.out_edges(u_for_edge, keys=True)) & set(self.in_edges(v_for_edge, keys=True))
        else:
            edges = set()
        if hsh not in (self.edges[e]['hsh'] for e in edges):
            # use an incremented integer as a unique key only if the edge is unique in attr and (u,v)
            return super(HierarchyGraph, self).add_edge(u_for_edge, v_for_edge, hsh=hsh, **attr)

    def surrounding_nodes(self, n):
        return list(self.parents.successors(n)) + list(self.parents.predecessors(n)) + [n]

    def _add_parent(self, child: Type[Hierarchy], parent: Union[Type[Hierarchy], Multiple]):
        relation, parent = normalise_relation(parent)
        relstyle = 'solid' if relation.maxnumber == 1 else 'dashed'
        self.add_edge(child, parent, singular=relation.maxnumber == 1, one2one=relation.one2one,
                       optional=relation.minnumber == 0, style=relstyle, actual_number=relation.maxnumber, type='is_child_of')
        if relation.one2one:
            self.add_edge(parent, child, singular=True, optional=relation.minnumber == 0, style='solid',
                          type='is_parent_of', one2one=relation.one2one,
                          relation=relation, actual_number=relation.maxnumber)
        else:
            self.add_edge(parent, child, singular=False, optional=relation.minnumber == 0, style='dashed',
                          type='is_parent_of', one2one=relation.one2one,
                          relation=relation, actual_number=relation.maxnumber)

    def _add_child(self, parent: Type[Hierarchy], child: Union[Type[Hierarchy], Multiple]):
        relation, child = normalise_relation(child)
        relstyle = 'solid' if relation.maxnumber == 1 else 'dashed'
        self.add_edge(parent, child, singular=relation.maxnumber == 1, one2one=relation.one2one,
                       optional=relation.minnumber == 0, type='is_parent_of',
                       relation=relation, style=relstyle, actual_number=relation.maxnumber)
        self.add_edge(child, parent, singular=True, optional=relation.minnumber == 0,  one2one=relation.one2one,
                      style='solid', type='is_child_of', actual_number=relation.maxnumber)

    def _add_self_reference(self, relation):
        relation, h = normalise_relation(relation)
        relstyle = 'solid' if relation.maxnumber == 1 else 'dashed'
        self.add_edge(h, h, singular=relation.maxnumber == 1, optional=relation.minnumber == 0,
                      one2one=relation.one2one, style=relstyle, actual_number=relation.maxnumber, type='is_parent_of')

    def _add_inheritance(self, hierarchy, base):
        self.add_edge(base, hierarchy, type='subclassed_by', actual_number=math.inf, style='dotted', optional=False,
                      one2one=False)
        self.add_edge(hierarchy, base, type='is_a', actual_number=0, style='dotted', optional=False, one2one=False)

    def _assign_edge_weights(self):
        for u, v, k, d in self.edges(data=True, keys=True):
            if d['type'] == 'is_a':
                weight = 0
            elif d['type'] == 'subclassed_by':
                weight = math.inf
            elif d['singular']:
                weight = 0
            else:
                weight = d['actual_number']
            self.edges[u, v, k]['weight'] = weight

    def _add_hierarchy(self, hierarchy: Type[Hierarchy]):
        """
        For a given hierarchy, traverse all its required inputs (parents and children)
        """
        self.add_node(hierarchy)
        for parent in hierarchy.parents:
            if normalise_relation(hierarchy)[1] is parent:
                self._add_self_reference(parent)
            else:
                self._add_parent(hierarchy, parent)
        for child in hierarchy.children:
            if normalise_relation(hierarchy)[1] is child:
                self._add_self_reference(child)
            else:
                self._add_child(hierarchy, child)
        for inherited in hierarchy.__bases__:
            if issubclass(inherited, Hierarchy):
                self._add_inheritance(hierarchy, inherited)

    def ancestor_subgraph(self, source):
        nodes = nx.ancestors(self.parents_and_subclassed_by, source)
        nodes.add(source)
        return nx.subgraph(self, nodes).copy()

    def subgraph_view(self, filter_node=no_filter, filter_edge=no_filter) -> 'HierarchyGraph':
        return nx.subgraph_view(self, filter_node, filter_edge)  # type: HierarchyGraph

    @property
    def inheritance(self):
        return self.subgraph_view(filter_edge=lambda *e: self.edges[e]['type'] == 'is_a').copy()

    @property
    def parents_and_subclassed_by(self):
        allowed = ['is_parent_of', 'subclassed_by']
        return self.subgraph_view(filter_edge=lambda *e: any(i == self.edges[e]['type'] for i in allowed)).copy()

    @property
    def parents(self):
        return self.subgraph_view(filter_edge=lambda *e: self.edges[e]['type'] == 'is_parent_of').copy()

    @property
    def children(self):
        return self.subgraph_view(filter_edge=lambda *e: self.edges[e]['type'] == 'is_child_of').copy()

    @property
    def parents_and_inheritance(self):
        return self.subgraph_view(filter_edge=lambda *e: self.edges[e]['type'] == 'is_parent_of' or 'is_a' == self.edges[e]['type']).copy()

    @property
    def children_and_inheritance(self):
        return self.subgraph_view(filter_edge=lambda *e: self.edges[e]['type'] == 'is_child_of' or 'is_a' == self.edges[e]['type']).copy()

    @property
    def traversal(self):
        def func(u, v):
            edge = self.edges[u, v]
            typ = edge['type']
            return (typ == 'is_parent_of') or (typ == 'is_a') or edge['one2one']
        return self.subgraph_view(filter_edge=func)

    @property
    def nonoptional(self):
        return self.subgraph_view(filter_edge=lambda *e: not self.edges[e]['optional']).copy()

    def sort_deepest(self, a, b):
        """Returns [a, b] or [b, a], in order of increasing depth in the graph"""
        if a is b:
            return a, b, self.parents_and_inheritance, False
        if a not in self:
            raise nx.NodeNotFound(f"Node {a} not found in graph {self}")
        if b not in self:
            raise nx.NodeNotFound(f"Node {b} not found in graph {self}")
        if b in nx.ancestors(self.parents_and_inheritance, a):
            return b, a, self.parents_and_inheritance, False
        elif a in nx.ancestors(self.parents_and_inheritance, b):
            return a, b, self.parents_and_inheritance, False
        elif a in nx.ancestors(self.children_and_inheritance, b):
            return a, b, self.children_and_inheritance, True
        elif b in nx.ancestors(self.children_and_inheritance, a):
            return b, a, self.children_and_inheritance, True
        else:
            raise nx.NetworkXNoPath(f"There is no path between {a} and {b}")

    def short_edge(self, a, b, weight=None):
        """return the shortest edge in the multidigraph for a given digraph edge """
        return pick_shortest_multiedge(self, a, b, weight)

    @property
    def singular(self):
        return self.subgraph_view(filter_edge=lambda *e: self.edges[e]['weight'] == 0)

    @property
    def plural(self):
        return self.subgraph_view(filter_edge=lambda *e: self.edges[e]['weight'] > 0)

    def find_paths(self, a, b, singular):
        """
        Returns a set of paths from a to b
        Not all paths are guaranteed to be valid, for example:
            l2superstack.ob yields paths of which `L2superstack-...-L1Single-...-ob` is one.
            This path is invalid, but it is included anyway because it is one of a set
            (through single,stack,superstack,supertarget).
            When queried, invalid paths will silently yield 0 results so it is not a problem.
        """
        try:
            return find_paths(self.singular.nonoptional, a, b)
        except nx.NetworkXNoPath:
            try:
                return find_paths(self.singular, a, b)
            except nx.NetworkXNoPath as e:
                if singular:
                    raise e
                try:
                    return find_paths(self.nonoptional, a, b)
                except nx.NetworkXNoPath:
                    return find_paths(self, a, b)

    def edge_weights(self, path) -> List[int]:
        return [self.edges[self.short_edge(*e, 'weight')]['weight'] for e in nx.utils.pairwise(path)]

    def edge_path_is_singular(self, edges):
        return not any(self.edges[e]['weight'] > 0 for e in edges)

    def path_is_singular(self, path) -> bool:
        return not any(e > 0 for e in self.edge_weights(path))

def get_all_class_bases(cls: Type[Graphable]) -> Set[Type[Graphable]]:
    new = set()
    for b in cls.__bases__:
        if b is Graphable or not issubclass(b, Graphable):
            continue
        new.add(b)
        new.update(get_all_class_bases(b))
    return new


from lxml import etree
import json, data_getter, re

#获取公司名称、详情链接、ID
def companies_info(context):
    """
    输入GET https://angel.co/companies/startups 得到的json数据
    输出该页面中的公司详情地址
    """
    context = context['html']
    result = []

    selector = etree.HTML(context)
    #读取出详情页面地址
    companies_page = selector.xpath("//a[@class='startup-link']")

    for page in companies_page:
        data = {
            "page"  : page.get("href"),
            "id"    : page.get("data-id"),
            "name"  : page.text
        }
        if page.text == None:
            continue
        else:
            result.append(data)
    #print(len(result))
    #print(result)
    return result

#反爬机制的token获取
def csrf_token(context):
    selector = etree.HTML(context)
    return selector.xpath("//meta[@name='csrf-token']/@content")

#获取某公司职位列表
def jobs_list(context):
    """
    输入公司职位页面
    返回一个字典对象
    {
        "工作类别": [
            {"name": "工作名字", "link":"工作详情页面", "type":"工作性质", "working_place":"工作地点", "salary":"薪资待遇"},...
        ],...
    }
    """
    with open("jobs_response.html", 'w', encoding='utf-8') as f:
         f.write(context)
    result = {}
    selector = etree.HTML(context)
    classes = selector.xpath("//ul[@class='job-listings']/li")
    #逐个遍历工作类别
    for li in classes:
        divs = li.getchildren()
        class_name = divs[0].text.strip("\n")
        result[class_name] = []
        jobs = divs[1].getchildren()
        #遍历类别中的每个职位
        for job in jobs:
            #职位名称
            job_name = job.getchildren()[0].getchildren()[0].getchildren()[0].text
            #职位详情链接
            job_link = job.getchildren()[0].getchildren()[0].getchildren()[0].get("href")
            job_detail = job.getchildren()[0].getchildren()[0].getchildren()[1].text.strip('\n')
            job_arr = job_detail.split('\n')
            #工作性质
            job_type = job_arr[2]
            #工作地点
            working_place = job_arr[0]
            #薪资待遇
            try:
                salary = job.getchildren()[0].getchildren()[0].getchildren()[1].getchildren()[1].text.split('\n')[2]
            except:
                with open("bad_select.html", 'w', encoding='utf-8') as f:
                    f.write(context)
                salary = 0
            result[class_name].append({
                "name"          : job_name,
                "link"          : job_link,
                "type"          : job_type,
                "working_place" : working_place,
                "salary"        : salary
            })
    return result

#获取公司详情
def company_detail(context):
    with open("detail_response.html", 'w', encoding='utf-8') as f:
         f.write(context)
    result = {}
    selector = etree.HTML(context)
    #公司简介
    company_name = selector.xpath("//h1/text()")
    result['name'] = company_name[0]
    content = selector.xpath("//div[@class='js-truncated-preamble s-vgBottom_5']/span/text()")
    if content or len(content) == 0:
        content1 = selector.xpath("//div[@class='content']/text()")
        #print(content1)
        content2 = selector.xpath("//div[@class='content']/span/text()")
        #print(content2)
        content = content1 + content2
    string = "".join(content)
    string = string.strip('\n')
    result['content'] = string
    #print(result['content'])

    #公司位置、标签
    tags = selector.xpath("//a[@class='tag']/text()")
    try:
        result['location'] = tags.pop(0)
    except:
        result["location"] = ""
    result['tags'] = tags

    #公司联系方式、公司规模（员工数量）、详细位置、公司主页
    text = selector.xpath("//script[@type='application/ld+json']/text()")
    info = json.loads(text[0])
    result['contact'] = info['sameAs']
    result['numberOfEmployees'] = info['numberOfEmployees']
    result['address'] = []
    for address in info['address']:
        try:
            result['address'].append({
                "addressCountry": address['addressCountry'],
                "addressLocality": address['addressLocality'],
                "addressRegion": address['addressRegion']
            })
        except:
            break
    result['font_page'] = info['url']

    #雇员信息
    urls = selector.xpath("//a[@class='view_all']/@href")
    for url in urls:
        if("/startup_roles?role=employee&startup_id") in url:
            result['employees'] = employees_info(data_getter.employees(url))
    #部分公司没有团队介绍，只能从招聘者取出雇员来作为团队
    try:
        _ = result['employees']
    except:
        """
        通过上面得到的info来获取founder的用户id
        根据用户id去访问
        https://angel.co/follows/tooltip?type=User&id=【id】
        会得到用户数据
        可能会有公司没有团队的介绍，但是每个公司都会有founder的数据
        """
        founders = info['founders']
        result['employees'] = []
        for founder in founders:
            image_url = founder['image']
            #print(image_url)
            search = re.search(r'.*/users/(.*)-medium.*', image_url)
            try:
                context = data_getter.user(search.group(1))
                result['employees'].append(user_info(context))
            except:
                continue
    for key in result:
        if result[key] is None:
            result[key] = ""
    return result

#获取公司雇员信息
def employees_info(employees):
    result = []
    for employee in employees["startup_roles/startup_profile"]:
        selector = etree.HTML(employee["html"])
        name = selector.xpath("//a[@class='profile-link']/text()")
        user_id = selector.xpath("//a[@class='profile-link']/@data-id")
        #部分人员没有链接，只有名字
        try:
            name = name[0]
            user_id = user_id[0]
        except:
            name = selector.xpath("//div[@class='name']/text()")
            try:
                name = name[0]
                name = name.strip("\n")
            except:
                name = ""
        else:
            title = selector.xpath("//div[@class='role_title']/text()")
            #部分人员没有填写职位信息
            try:
                title = title[0]
            except:
                title = ""
            bio = selector.xpath("//div[@class='bio']/p/text()")
            #部分人员没有描述信息
            try:
                bio = bio[0]
            except:
                bio = ""
            result.append({
                "id"    : user_id,
                "name"  : name,
                "title" : title,
                "bio"   : bio.strip('\n')
            })
    return result

#获取用户信息
def user_info(context):
    selector = etree.HTML(context)
    name = selector.xpath("//a[@class='profile-link']/text()")
    user_id = selector.xpath("//a[@class='profile-link']/@data-id")
    name = name[0]
    title = ""
    bio = selector.xpath("//div[@class='body']/text()")
    bio = bio[0]

    return {
        "id"    : user_id[0],
        "name"  : name,
        "title" : title,
        "bio"   : bio
    }

#测试代码
if __name__ == "__main__":
    #test company_detail
    context = ""
    with open("detail_response.html", encoding='utf-8') as f:
        context = f.read()
    print(company_detail(context))

    #test employees_info
    # employees = {}
    # with open("example/employees.json") as f:
    #     employees = json.load(f)
    # print(employees_info(employees))

    #test companies_info
    # context = {}
    # with open("example/companies.html") as f:
    #     context = json.load(f)
    # print(companies_info(context))

    #test jobs_list
    # context = ""
    # with open("example/jobs.html") as f:
    #     context = f.read()
    # print(jobs_list(context))

    pass