import random
import math
import matplotlib.pyplot as plt

MAPWIDTH = 100 # 地图的宽度
MAPLEN = 200 # 地图的长度

class Path:
    '''represent hamiltonian cycle.

    attributes:
    num(int):num of existing edges
    path(list):sequence of city ID listed in city_map
    city_map(CityMap):related map of cities
    '''
    def __init__(self, city_map, path_list=[]):
        self.num = city_map.num
        self.city_map = city_map
        if len(path_list) == 0:
            self.path = list(range(self.num))
            random.shuffle(self.path)
        else:
            self.path = path_list.copy()

    def get_distance(self):
        '''return total distance of the cycle.'''
        distance = 0
        for i in range(self.num):
            distance += self.city_map.map[self.path[i]][self.path[(i+1)%self.num]]
        return distance

    def crossover_path(self, partner):
        '''crossover with partner and return nothing.

        args:
        partner(Path):path to crossover with
        '''
        min = random.randint(0, self.num-1)
        max = random.randint(min, self.num-1)
        self.path[min: max+1], partner.path[min: max+1] = \
        partner.path[min: max+1], self.path[min: max+1]
        # 交换去重
        check_area = list(range(self.num))[0: min] +\
                     list(range(self.num))[max+1: self.num]
        ptn_s = 0
        for i in check_area:
            if self.path[i] in self.path[min:max+1]:
                for j in check_area[ptn_s:]:
                    if partner.path[j] in partner.path[min:max+1]:
                        self.path[i], partner.path[j] =\
                        partner.path[j], self.path[i]
                        ptn_s += 1
                        break
                    ptn_s += 1

class CityMap:
    '''representation of graph of cities.'''
    def __init__(self, num):
        self.num = num
        # 产生一个初始的城市地图，采用邻接矩阵来存储
        self.city = []
        for i in range(self.num):
            self.city.append((random.randrange(MAPWIDTH),
                              random.randrange(MAPLEN)))
        # 先假设每个城市之间都有路
        self.map = [[0]*self.num for i in range(self.num)]
        for i in range(self.num):
            for j in range(self.num):
                self.map[i][j] = distance(self.city[i],
                                          self.city[j])

class Group:
    '''representation of Group in revolution.'''

    def __init__(self, city_map, init_size):
        '''constructor of Group class.

        args:
        city_map(CityMap):city_map that the group is based on
        init_size(int):initial size of group
        '''
        self.city_map = city_map # 委托模式，仅仅是一个引用而不是新的对象
        self.init_size = init_size
        self.path = [Path(city_map) for i in range(init_size)]

    def score(self, save_rate=1.0):
        '''evaluate every path in the group and return a list of score.

        args:
        save_rate(float):highest surviving possibility
        '''
        result = []
        for i in range(self.init_size):
            result.append(float(1)/self.path[i].get_distance())
        max_possibility = max(result)
        result = [i / max_possibility * save_rate for i in result]
        return result

    def revolve(self, variation_rate):
        '''implement revolution.

        args:
        variation_rate(float):ratio of variate genes
        '''
        variation_rate = int(variation_rate * self.city_map.num)
        score = self.score()
        max_index = score.index(max(score))
        result = []
        for i in range(self.init_size):
            if random.random() > score[i]: # 把这一项变异掉
                list = self.path[max_index].path.copy()
                for i in range(variation_rate):
                    a = random.randrange(0, self.city_map.num)
                    b = random.randrange(0, self.city_map.num)
                    list[a], list[b] = list[b], list[a]
                self.path[i] = Path(self.city_map, list)
                result.append(0)
            else:
                result.append(1)
        good_index = [i[0] for i in enumerate(result) if i[1] == 1]
        random.shuffle(good_index)
        for i in range(int(len(good_index)/2)):
            self.path[good_index[2*i]].crossover_path(self.path[good_index[2*i+1]])

    def show(self, choice=0):
        '''display path as a matplotlib pic.

        args:
        choice(int):choose the path to display

        note:
        if choice missing then display the shortest path in the group.
        parameter choice should be within the range of 0~num or will arise Exception.
        '''
        if(choice == 0):
            score = self.score()
            choice = score.index(max(score))
        elif choice>0 and choice<len(self.path)+1:
            choice -= 1
        else:
            raise(Exception, "Wrong choice value.")

        plt.figure(figsize = (10, 10), dpi = 100)
        x = [i[0] for i in self.city_map.city]
        y = [i[1] for i in self.city_map.city]
        plt.scatter(x, y)
        for i in range(self.path[choice].num):
            plt.plot([self.city_map.city[self.path[choice].path[i]][0],\
                      self.city_map.city[self.path[choice].path[(i+1)%self.path[choice].num]][0]],\
                     [self.city_map.city[self.path[choice].path[i]][1],\
                      self.city_map.city[self.path[choice].path[(i+1)%self.path[choice].num]][1]])
        plt.show()

def distance(city1, city2):
    '''caculate and return distance between two cities.'''
    return math.sqrt(math.pow(city1[0]-city2[0], 2)+math.pow(city1[1]-city2[1], 2))

if __name__== '__main__':
    init_size = 1000
    epoch = 20

    city_map = CityMap(100)
    group = Group(city_map, init_size)
    result = []
    group.show()
    for i in range(epoch):
        group.revolve(10)
        score = group.score(0.8)
        min_length = group.path[score.index(max(score))].get_distance()
        print("min length:", min_length)
        result.append(min_length)
    group.show()
    plt.plot(result)
    plt.show()


"""
RL-Scope configuration settings.

``DEBUG_*`` are useful for enabling certain debug logging statements.
:py:class:`_EnvVars` is for reading environment variables.
"""
from os.path import join as _j, abspath as _a, exists as _e, dirname as _d, basename as _b
from os import environ as ENV
import shutil
import re
import ctypes.util
import sys
import os
import textwrap

# NOTE: Don't import logger here since rlscope_logging needs py_config.
# from rlscope.profiler.rlscope_logging import logger
import rlscope

INSTALL_ROOT = _d(os.path.realpath(rlscope.__file__))

CPP_LIB = _j(INSTALL_ROOT, 'cpp', 'lib')
CPP_BIN = _j(INSTALL_ROOT, 'cpp', 'bin')
CPP_INCLUDE = _j(INSTALL_ROOT, 'cpp', 'include')

USE_NUMBA = False
nb = None
if USE_NUMBA:
  import numba as nb

ROOT = _d(_d(_a(__file__)))

RLSCOPE_DIR = ROOT

GIT_REPO_URL = "https://github.com/UofT-EcoSystem/rlscope"

RLSCOPE_TEST_DIR = _j(RLSCOPE_DIR, 'test_results')

DEBUG = False

VERSION_TXT = _j(ROOT, "version.txt")

CLONE = _j(ENV['HOME'], 'clone')
BASELINES_ROOT = _j(CLONE, 'baselines')
BENCH_DQN_PY = _j(BASELINES_ROOT, 'baselines/deepq/experiments/benchmark_dqn.py')

SQL_IMPL = 'psql'
assert SQL_IMPL in {'psql', 'sqlite'}

ANALYSIS_PY = _j(ROOT, "python/scripts", "analyze.py")
GENERATE_INDEX_PY = _j(ROOT, "python/scripts", "generate_rlscope_plot_index.py")

CPP_UNIT_TEST_CMD = 'rls-test'

# If true, when you provide --rlscope-debug, log messages when:
# - A session is created by calling tf.Session()
# - A session is used by calling sess.run()
# - RL-Scope trace-data belonging to a session is dumped
DEBUG_TRACE_SESSION = False

# Verbose debugging: Print calls to set_operation/end_operation
DEBUG_OPERATIONS = False

# Verbose debugging: Print calls to librlscope.so
DEBUG_RLSCOPE_LIB_CALLS = False

# If true, have rls-util-sampler log to stdout the GPU/CPU info it queries.
# This can be noisy.
DEBUG_UTIL_SAMPLER = False

# If true, dump stack when we initially start recording progress
# for a phase during rlscope.prof.report_progress(...)
DEBUG_REPORT_PROGRESS = False
# If true, dump stack for ALL calls to rlscope.prof.report_progress(...) that update progress
# (not just the first call for a phase)
DEBUG_REPORT_PROGRESS_ALL = False

# If True, print out how event are grouped into OpStack's when split_op_stacks.
DEBUG_SPLIT_STACK_OPS = False
# If True, skip inserting any profiling-overhead events.
DEBUG_SKIP_PROFILING_OVERHEAD = False

DEBUG_GPU_HW = False

# If True, then log all calls into librlscope.so
# DEBUG_SAMPLE_CUDA_API = True

# If True, then log all calls into a wrapped library (e.g. TensorFlow, simulator, etc.).
# DEBUG_C_LIB_CALLS = True

# In case docker causes different runtime behaviour, we can use this switch.
#
# Current issues:
# - nvidia-smi doesn't work properly from within a container.
#   Docker uses a DIFFERENT pid namespace within the container.
#   i.e. the pid of the proc in the container will be DIFFERENT
#   from the proc outside the the container, when you look at it with 'top'.
#   HOWEVER, nvidia-smi within the container still reports the pid of process
#   OUTSIDE the container.
#   - This bug affects GPU memory/compute utilization sampling;
#     if we're in a docker container just sample the entire memory usage of a GPU.
IS_DOCKER = False
if 'RLSCOPE_LOGGED_IN' in ENV:
  IS_DOCKER = True

# Print the names of all the TensorFlow/Simulator function calls that get wrapped.
DEBUG_WRAP_CLIB = False

# Print information about delays that happen during Profiler.finish().
DEBUG_CRITICAL_PATH = False

# LIB_SAMPLE_CUDA_API = None
# if _e(_j(ROOT, 'Debug', RLSCOPE_LIB)):
#   LIB_SAMPLE_CUDA_API = _j(ROOT, 'Debug', RLSCOPE_LIB)
# elif _e(_j(ROOT, 'Release', RLSCOPE_LIB)):
#   LIB_SAMPLE_CUDA_API = _j(ROOT, 'Release', RLSCOPE_LIB)
# elif _e(_j(ROOT, 'build', RLSCOPE_LIB)):
#   LIB_SAMPLE_CUDA_API =  _j(ROOT, 'build', RLSCOPE_LIB)

# Use a custom-built/modified version of TF for benchmarking things.
# Modifies C++ code to make tfprof add less overhead to the critical path.
# CUSTOM_TF = True
# CUSTOM_TF = False

class _EnvVars:
  """
  Typed wrapper around os.environ

  # E.g.
  $ export DEBUG=yes
  # OR
  $ export DEBUG=true
  ...
  >>> EnvVars.get_bool('DEBUG')
  True

  # E.g.
  $ export DEBUG=0
  # OR
  $ export DEBUG=false
  ...
  >>> EnvVars.get_bool('DEBUG')
  False
  """
  def __init__(self, env=None):
    if env is None:
      env = os.environ
    self.env = env

  def _as_bool(self, string):
    string = string.lower()
    if string in {'0', 'no', 'false', 'None'}:
      return False
    return True

  def _as_int(self, string):
    return int(string)

  def _as_float(self, string):
    return float(string)

  def _as_string(self, string):
    return string

  def _as_number(self, string):
    try:
      val = int(string)
      return val
    except ValueError:
      pass

    val = float(string)
    return val

  def _get_typed(self, var, converter, dflt):
    if var not in self.env:
      return dflt
    string = self.env[var]
    val = converter(string)
    return val

  def get_string(self, var, dflt=None):
    val = self._get_typed(var, self._as_string, dflt)
    return val

  def get_bool(self, var, dflt=False):
    val = self._get_typed(var, self._as_bool, dflt)
    return val

  def get_int(self, var, dflt=0):
    val = self._get_typed(var, self._as_int, dflt)
    return val

  def get_float(self, var, dflt=0.):
    val = self._get_typed(var, self._as_float, dflt)
    return val

  def get_number(self, var, dflt=0.):
    val = self._get_typed(var, self._as_number, dflt)
    return val
EnvVars = _EnvVars(env=ENV)

NUMBA_DISABLE_JIT = EnvVars.get_bool('NUMBA_DISABLE_JIT', dflt=False)

RLSCOPE_DISABLE_JIT = EnvVars.get_bool('RLSCOPE_DISABLE_JIT', dflt=False)

# Use numbafied implementation of event overlap computation.
RLSCOPE_USE_NUMBA = EnvVars.get_bool('RLSCOPE_USE_NUMBA', dflt=False)
# More verbose debugging information during unit-tests.
RLSCOPE_DEBUG_UNIT_TESTS = EnvVars.get_bool('RLSCOPE_DEBUG_UNIT_TESTS', dflt=False)
RLSCOPE_DEBUG_UNIQUE_SPLITS_BASE = EnvVars.get_string('RLSCOPE_DEBUG_UNIQUE_SPLITS_BASE', dflt=None)

HOST_INSTALL_PREFIX = _j(ROOT, "local.host")
DOCKER_INSTALL_PREFIX = _j(ROOT, "local.docker")

HOST_BUILD_PREFIX = _j(ROOT, "build.host")
DOCKER_BUILD_PREFIX = _j(ROOT, "build.docker")

# NOTE: Don't touch this, it gets set manually by unit-tests.
IS_UNIT_TEST = False

# NUMPY_TIME_USEC_TYPE = np.uint64
# NUMBA_TIME_USEC_TYPE = nb.uint64
#
# NUMPY_CATEGORY_KEY_TYPE = np.uint64
# NUMBA_CATEGORY_KEY_TYPE = nb.uint64

# NOTE: numba likes to assume np.int64 as the default platform type when dealing with integers
# (e.g. indices in a "for in range(n)" loop, "x = 0" statements).
# As a result, attempts to use np.uint64 result in lots of compilation errors.
# I haven't bothered too hard to try to fix them... hopefully an int64 is big enough for our purposes...
# should probably add a check for that!

if USE_NUMBA:
  import numpy as np
  NUMPY_TIME_USEC_TYPE = np.int64
  NUMBA_TIME_USEC_TYPE = nb.int64

  NUMPY_TIME_PSEC_TYPE = np.int64
  NUMBA_TIME_PSEC_TYPE = nb.int64

  NUMPY_CATEGORY_KEY_TYPE = np.int64
  NUMBA_CATEGORY_KEY_TYPE = nb.int64

def is_development_mode():
  return shutil.which('rlscope-is-development-mode') is not None

def is_production_mode():
  return not is_development_mode()

def yes_as_bool(yes_or_no):
  if yes_or_no.lower() in {'yes', 'y', 'on', '1'}:
    return True
  return False

def is_running_unit_tests():
  return yes_as_bool(os.environ.get('RLS_RUNNING_UNIT_TESTS', 'no'))

def read_rlscope_version():
  lines = []
  with open(VERSION_TXT) as f:
      for line in f:
        line = line.rstrip()
        if re.search(r'^\s*#.*', line):
          continue
        lines.append(line)
  if len(lines) == 0:
    return None
  return ''.join(lines)

if 'pytest' in sys.modules and 'RLS_RUNNING_UNIT_TESTS' not in os.environ:
  # Prevent users from running with "pytest" so we can detect in-code if we're running with pytest.
  # Useful for avoiding loading modules where optional imports may be used (e.g., "import tensorflow").
  # logger.error(textwrap.dedent("""
  sys.stderr.write(textwrap.dedent("""\
  Don't run \"pytest\" directly; instead:

    # Run Python unit tests using pytest:
    $ rls-unit-tests --tests py

    # Or, run Python and C++ unit tests:
    $ rls-unit-tests
  """))
  sys.exit(1)


"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', views.index),
    url(r'^index/', views.index),
    url(r'^register/', views.register),
    url(r'^login/', views.login),
    url(r'^logout/', views.logout),
    url(r'^publish/', views.publish),
    url(r'^publish_text/', views.publish_text),
    url(r'^gain/', views.gain),
    url(r'^login_confirm/', views.login_confirm),
    url(r'^recommend/', views.recommend),
    url(r'^favorite/', views.favorite),
    url(r'^comment/', views.comment),
    url(r'^make_comment/', views.make_comment),
    url(r'^page/', views.page),
]


import torch
from lime.lime_text import LimeTextExplainer

from methods.default import DefaultMethod
import math

import numpy as np

import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

class LimeExplainer(DefaultMethod):

    def __init__(self, model, dataset, threshold, model_type="transformer_foreginer", query="What is the sentiment of this review?", class_names=['negative', 'positive']):
        super(LimeExplainer, self).__init__(model, dataset, threshold, query)
        self._J=0
        self.expl = LimeTextExplainer(class_names=class_names,split_expression=" ", bow=False, random_state=42)
        self.model_type = model_type

    def scores(self, dataset):

        features, attentions = [], []
        labels = torch.ones(1, dtype=torch.int64).to(self.model.device)
        inputs = dataset['text']

        inputs_in , always_keep_masks, kept_tokens = self.model.create_batchs(inputs, 1, self.query)

        metadatas = []
        if self.query != None:
            query_tokens = ["[SEP]"] + self.query.split() + ["[SEP]"]
        else:
            query_tokens = ["[SEP]"]
        for i in range(len(inputs)):
            metadata = {}
            always_keep_mask = np.array(always_keep_masks[i])
            metadata['always_keep_mask'] = always_keep_mask
            metadata['convert_tokens_to_instance'] = self.model.convert_tokens_to_instance
            metadata['tokens'] = list(filter(lambda x: bool(len(x)), inputs[i].strip().split(' '))) + query_tokens
            metadatas.append(metadata)

        attentions = []
        predicted_labels = []
        probabilite = []
        for batch in inputs_in:
            outputs = self.model.forward(document=batch, metadata=None, label=labels)
            predicted_labels.extend(outputs["predicted_labels"].detach().cpu())
            probabilite.extend(outputs["probs"].detach().cpu())
            del outputs
            torch.cuda.empty_cache()

        def predic_prob(inp_texts):

            filtered_tokens = [
                    [int(t) for t in selected_tokens.split(" ") if t != "UNKWORDZ"] for selected_tokens in inp_texts
                ]

            #inputs_in, _, _ = create_batch(inp_texts, 1, inp_stream)
            probas = []
            for i in range(len(filtered_tokens)):

                document = self.model.lime_tokenizer(metadatas[self._J], always_keep_masks[self._J], filtered_tokens[i])
                outputs = self.model.forward(document=document, metadata=None, label=torch.ones(1, dtype=torch.int64).to(self.model.device))
                probas.append(outputs["probs"].detach().cpu())

                del outputs
                torch.cuda.empty_cache()

            probas = torch.cat(probas, dim=0)
            return probas

        indice=0
        self._J=0
        for row in inputs:
            logging.info("INPUT "+str(indice))
            predicted_label = predicted_labels[indice].cpu().tolist()

            always_keep_mask = always_keep_masks[indice]

            selection_tokens = [i for i, x in enumerate(always_keep_mask) if x != 1]

            num_features = math.ceil(self._threshold * len(selection_tokens))

            explanation = self.expl.explain_instance(
                " ".join([str(i) for i in selection_tokens]),
                predic_prob,
                num_features=min(len(selection_tokens), num_features*2),
                labels=(predicted_label,),
                num_samples=1000,
            )

            weights=explanation.as_list(predicted_label)

            saliency = [0.0 for _ in range(len(metadatas[self._J]['tokens']))]
            for f, w in weights:
                saliency[int(f)] = max(0.0, w)

            #Calculate the attention
            saliency = torch.Tensor([saliency]).to(self.model.device)
            attention = saliency
            k_p = torch.zeros(kept_tokens[indice].shape[1] - attention.shape[1]).to(self.model.device)
            attention = torch.cat((attention[0], k_p)).unsqueeze(0).to(self.model.device)

            attn1 = attention * (1 - kept_tokens[indice].to(self.model.device)).float()
            inter_attn = attn1 / attn1.sum(-1, keepdim=True)
            attentions.append(inter_attn.cpu().data.tolist())
            indice+=1
            self._J+=1

        score = [item for sublist in attentions for item in sublist]

        return {"features":features, "scores":score, "predicted_labels": predicted_labels, "probas": probabilite, "always_keep_masks": always_keep_masks, "metadatas":metadatas}

    def visualize(self, dataset, top_k=None):
        inputs = dataset["text"]
        labels = torch.tensor(dataset["label"]).to(self.model.device)

        inputs_in , always_keep_masks, kept_tokens = self.model.create_batchs(inputs, 1, self.query)

        metadatas = []
        if self.query != None:
            query_tokens = ["SEP"] + self.query.split() + ["SEP"]
        else:
            query_tokens = []
        for i in range(len(inputs)):
            metadata = {}
            always_keep_mask = np.array(always_keep_masks[i])
            metadata['always_keep_mask'] = always_keep_mask
            metadata['convert_tokens_to_instance'] = self.model.convert_tokens_to_instance
            metadata['tokens'] = list(filter(lambda x: bool(len(x)), inputs[i].strip().split(' '))) + query_tokens
            metadatas.append(metadata)

        predicted_labels = []
        i = 0
        for batch in inputs_in:
            outputs = self.model.forward(document=batch, metadata=None, label=labels[i])
            predicted_labels.extend(outputs["predicted_labels"].detach().cpu())
            del outputs
            torch.cuda.empty_cache()
            i+=1

        def predic_prob(inp_texts):
            filtered_tokens = [
                    [t for t in selected_tokens.split(" ") if t != "UNKWORDZ"] for selected_tokens in inp_texts
                ]

            #inputs_in, _, _ = create_batch(inp_texts, 1, inp_stream)
            probas = []
            for i in range(len(filtered_tokens)):

                document = self.model.lime_tokenizer_viz(metadatas[self._J], always_keep_masks[self._J], filtered_tokens[i])
                outputs = self.model.forward(document=document, metadata=None, label=torch.ones(1, dtype=torch.int64).to(self.model.device))
                probas.extend(outputs["probs"].detach().cpu().tolist())

                del outputs
                torch.cuda.empty_cache()

            return np.array(probas)

        self._J=0
        for inp in inputs:
            predicted_label = predicted_labels[self._J].cpu().tolist()

            always_keep_mask = always_keep_masks[self._J]

            selection_tokens = [i for i, x in enumerate(always_keep_mask) if x != 1]

            num_features = math.ceil(self._threshold * len(selection_tokens))

            explanation = self.expl.explain_instance(
                    inp,
                    predic_prob,
                    num_features=min(len(selection_tokens), num_features*2),
                    labels=(predicted_label,),
                    num_samples=1000,
                )

            explanation.show_in_notebook()
            explanation.save_to_file("output_"+str(self._J)+".html", labels=(predicted_label,))

            self._J+=1


"""Manage software dependencies"""
import glob
import logging
import os
import platform
import subprocess
import sys
import tempfile
import textwrap
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from distutils.version import LooseVersion
from shutil import which

import distro

from . import config, defs, util

logger = logging.getLogger(__name__)
runner = util.ProcessRunner(logger)

target_map = {
    "sdr": defs.sdr_setup_type,
    "usb": defs.linux_usb_setup_type,
    "standalone": defs.standalone_setup_type,
    "sat-ip": defs.sat_ip_setup_type
}
supported_distros = ["ubuntu", "debian", "raspbian", "fedora", "centos"]

def _check_distro(setup_type):
    """Check if distribution is supported"""
    if (distro.id() in supported_distros):
        return

    base_url = defs.user_guide_url + "doc/"
    instructions_url = {
        defs.sdr_setup_type: "sdr.md",
        defs.linux_usb_setup_type: "tbs.md",
        defs.standalone_setup_type: "s400.md",
        defs.sat_ip_setup_type: "sat-ip.md"
    }
    full_url = base_url + instructions_url[setup_type]
    logger.error("{} is not a supported Linux distribution".format(
        distro.name()))
    logger.info(
        textwrap.fill("Please, refer to the {} receiver setup "
                      "instructions at {}".format(setup_type, full_url)))
    raise ValueError("Unsupported Linux distribution")

def _get_apt_repo(distro_id):
    """Find the APT package repository for the given distro"""
    if distro_id == "ubuntu":
        apt_repo = "ppa:blockstream/satellite"
    elif distro_id in ["debian", "raspbian"]:
        apt_repo = ("https://aptly.blockstream.com/"
                    "satellite/{}/").format(distro_id)
    else:
        raise ValueError("Unsupported distribution {}".format(distro_id))
    return apt_repo

def _check_pkg_repo(distro_id):
    """Check if Blockstream Satellite's binary package repository is enabled

    Args:
        distro_id: Linux distribution ID

    Returns:
        (bool) True if the repository is already enabled

    """
    found = False
    if (which("apt")):
        apt_repo = _get_apt_repo(distro_id)
        grep_str = apt_repo.replace("ppa:", "")
        apt_sources = glob.glob("/etc/apt/sources.list.d/*")
        apt_sources.append("/etc/apt/sources.list")
        cmd = ["grep", grep_str]
        cmd.extend(apt_sources)
        res = runner.run(cmd, stdout=subprocess.DEVNULL, nocheck=True)
        found = (res.returncode == 0)
    elif (which("dnf")):
        res = runner.run(["dnf", "copr", "list", "--enabled"],
                         root=True,
                         capture_output=True)
        pkgs = res.stdout.decode().splitlines()
        found = ("copr.fedorainfracloud.org/blockstream/satellite" in pkgs)
    elif (which("yum")):
        yum_sources = glob.glob("/etc/yum.repos.d/*")
        cmd = ["grep", "blockstream/satellite"]
        cmd.extend(yum_sources)
        res = runner.run(cmd, stdout=subprocess.DEVNULL, nocheck=True)
        found = (res.returncode == 0)
    else:
        raise RuntimeError("Could not find a supported package manager")

    if (found):
        logger.debug("blockstream/satellite repository already enabled")

    return found

def _enable_pkg_repo(distro_id, interactive):
    """Enable Blockstream Satellite's binary package repository

    Args:
        distro_id: Linux distribution ID
        interactive: Interactive mode

    """
    cmds = list()
    if (which("apt")):
        apt_repo = _get_apt_repo(distro_id)

        # On debian, add the apt repository through "add-apt-repository". On
        # raspbian, where "add-apt-repository" does not work, manually add a
        # file into /etc/apt/sources.list.d/.
        if distro_id == "raspbian":
            release_codename = distro.codename()
            apt_list_filename = "blockstream-satellite-{}-{}.list".format(
                distro_id, release_codename)
            apt_list_file = os.path.join("/etc/apt/sources.list.d/",
                                         apt_list_filename)
            apt_component = "main"
            apt_file_content = "deb {0} {1} {2}\n# deb-src {0} {1} {2}".format(
                apt_repo, release_codename, apt_component)
            # In execution mode, create a temporary file and move it to
            # /etc/apt/sources.list.d/. With that, only the move command needs
            # root permissions, whereas the temporary file does not. In dry-run
            # mode, print an equivalent echo command.
            if (runner.dry):
                cmd = [
                    "echo", "-e",
                    repr(apt_file_content), ">>", apt_list_file
                ]
            else:
                tmp_file = tempfile.NamedTemporaryFile(mode="w", delete=False)
                with tmp_file as fd:
                    fd.write(apt_file_content)
                cmd = ["mv", tmp_file.name, apt_list_file]
        else:
            cmd = ["add-apt-repository", apt_repo]
            if (not interactive):
                cmd.append("-y")

        cmds.append(cmd)

        if distro_id in ["debian", "raspbian"]:
            cmd = [
                "apt-key", "adv", "--keyserver", "keyserver.ubuntu.com",
                "--recv-keys", defs.blocksat_pubkey
            ]
            cmds.append(cmd)

        cmd = ["apt", "update"]
        if (not interactive):
            cmd.append("-y")
        cmds.append(cmd)
    elif (which("dnf")):
        cmd = ["dnf", "copr", "enable", "blockstream/satellite"]
        if (not interactive):
            cmd.append("-y")
        cmds.append(cmd)
    elif (which("yum")):
        cmd = ["yum", "copr", "enable", "blockstream/satellite"]
        if (not interactive):
            cmd.append("-y")
        cmds.append(cmd)
    else:
        raise RuntimeError("Could not find a supported package manager")

    for cmd in cmds:
        runner.run(cmd, root=True)

def _update_pkg_repo(interactive):
    """Update APT's package index

    NOTE: this function updates APT only. On dnf/yum, there is no need to run
    an update manually. The tool (dnf/yum) updates the package index
    automatically when an install/upgrade command is called.

    """

    if (not which("apt")):
        return

    cmd = ["apt", "update"]
    if (not interactive):
        cmd.append("-y")

    runner.run(cmd, root=True)

def _install_packages(apt_list,
                      dnf_list,
                      yum_list,
                      interactive=True,
                      update=False):
    """Install binary packages

    Args:
        apt_list    : List of package names for installation via apt
        dnf_list    : List of package names for installation via dnf
        dnf_list    : List of package names for installation via yum
        interactive : Whether to run an interactive install (w/ user prompting)
        update      : Whether to update pre-installed packages instead

    """
    if (which("apt")):
        manager = "apt"
        cmd = ["apt-get", "install"]
        if (update):
            cmd.append("--only-upgrade")
        cmd.extend(apt_list)
    elif (which("dnf")):
        manager = "dnf"
        if (update):
            cmd = ["dnf", "upgrade"]
        else:
            cmd = ["dnf", "install"]
        cmd.extend(dnf_list)
    elif (which("yum")):
        manager = "yum"
        if (update):
            cmd = ["yum", "upgrade"]
        else:
            cmd = ["yum", "install"]
        cmd.extend(yum_list)
    else:
        raise RuntimeError("Could not find a supported package manager")

    env = None
    if (not interactive):
        cmd.append("-y")
        if (manager == "apt"):
            env = os.environ.copy()
            env["DEBIAN_FRONTEND"] = "noninteractive"

    runner.run(cmd, root=True, env=env)

def _install_common(interactive=True, update=False, btc=False):
    """Install dependencies that are common to all setups"""
    util.print_header("Installing Common Dependencies")
    apt_pkg_list = ["software-properties-common"]
    dnf_pkg_list = ["dnf-plugins-core"]
    yum_pkg_list = ["yum-plugin-copr"]

    # "gnupg" is installed as a dependency of "software-properties-common" on
    # Ubuntu. In contrast, it is not a dependency on Debian. Hence, add it
    # explicitly to the list. Add also "dirmngr", which may not be
    # automatically installed with gnupg (it is only automatically installed
    # from buster onwards). Lastly, add "apt-transport-https" to fetch debian
    # and raspbian packages from Aptly using HTTPS.
    distro_id = distro.id()
    if distro_id in ["debian", "raspbian"]:
        apt_pkg_list.extend(["gnupg", "dirmngr", "apt-transport-https"])

    if distro_id == "centos":
        dnf_pkg_list.append("epel-release")
        yum_pkg_list.append("epel-release")

    _install_packages(apt_pkg_list, dnf_pkg_list, yum_pkg_list, interactive,
                      update)

    # Enable our binary package repository
    if runner.dry or (not _check_pkg_repo(distro_id)):
        _enable_pkg_repo(distro_id, interactive)

    # Install bitcoin-satellite
    if (btc):
        apt_pkg_list = [
            "bitcoin-satellite", "bitcoin-satellite-qt", "bitcoin-satellite-tx"
        ]
        dnf_pkg_list = ["bitcoin-satellite", "bitcoin-satellite-qt"]
        yum_pkg_list = ["bitcoin-satellite", "bitcoin-satellite-qt"]

        if (which("dnf")):
            # dnf matches partial package names, and so it seems that when
            # bitcoin-satellite and bitcoin-satellite-qt are installed in one
            # go, only the latter gets installed. The former is matched to
            # bitcoin-satellite-qt and assumed as installed. Hence, as a
            # workaround, install the two packages separately.
            for pkg in dnf_pkg_list:
                _install_packages(apt_pkg_list, [pkg], [pkg], interactive,
                                  update)
        else:
            _install_packages(apt_pkg_list, [], yum_pkg_list, interactive,
                              update)

def _install_specific(target, interactive=True, update=False):
    """Install setup-specific dependencies"""
    key = next(key for key, val in target_map.items() if val == target)
    pkg_map = {
        'sdr': {
            'apt': ["gqrx-sdr", "rtl-sdr", "leandvb", "tsduck"],
            'dnf': ["gqrx", "rtl-sdr", "leandvb", "tsduck"],
            'yum': ["rtl-sdr", "leandvb", "tsduck"]
        },
        'usb': {
            'apt': ["iproute2", "iptables", "dvb-apps", "dvb-tools"],
            'dnf': ["iproute", "iptables", "dvb-apps", "v4l-utils"],
            'yum': ["iproute", "iptables", "dvb-apps", "v4l-utils"]
        },
        'standalone': {
            'apt': ["iproute2", "iptables"],
            'dnf': ["iproute", "iptables"],
            'yum': ["iproute", "iptables"]
        },
        'sat-ip': {
            'apt': ["iproute2", "tsduck"],
            'dnf': ["iproute", "tsduck"],
            'yum': ["iproute", "tsduck"]
        }
    }
    # NOTE:
    # - leandvb and tsduck come from our repository. Also, note gqrx is not
    #   available on CentOS (hence not included on the yum list).
    # - In the USB setup, the only package from our repository is dvb-apps in
    #   the specific case of dnf (RPM) installation, given that dvb-apps is not
    #   available on the latest mainstream fedora repositories.

    util.print_header("Installing {} Receiver Dependencies".format(target))
    _install_packages(pkg_map[key]['apt'], pkg_map[key]['dnf'],
                      pkg_map[key]['yum'], interactive, update)

def _print_help(args):
    """Re-create argparse's help menu"""
    parser = ArgumentParser()
    subparsers = parser.add_subparsers(title='', help='')
    parser = subparser(subparsers)
    print(parser.format_help())

def subparser(subparsers):
    """Parser for install command"""
    p = subparsers.add_parser('dependencies',
                              aliases=['deps'],
                              description="Manage dependencies",
                              help='Manage dependencies',
                              formatter_class=ArgumentDefaultsHelpFormatter)

    p.set_defaults(func=_print_help)
    p.add_argument("-y",
                   "--yes",
                   action='store_true',
                   default=False,
                   help="Non-interactive mode. Answers \"yes\" automatically \
                   to binary package installation prompts")
    p.add_argument("--dry-run",
                   action='store_true',
                   default=False,
                   help="Print all commands but do not execute them")

    subsubp = p.add_subparsers(title='subcommands', help='Target sub-command')

    p1 = subsubp.add_parser('install',
                            description="Install software dependencies",
                            help='Install software dependencies')
    p1.add_argument("--target",
                    choices=list(target_map.keys()),
                    default=None,
                    help="Target setup type for installation of dependencies")
    p1.add_argument("--btc",
                    action='store_true',
                    default=False,
                    help="Install bitcoin-satellite")
    p1.set_defaults(func=run, update=False)

    p2 = subsubp.add_parser('update',
                            aliases=['upgrade'],
                            description="Update software dependencies",
                            help='Update software dependencies')
    p2.add_argument("--target",
                    choices=list(target_map.keys()),
                    default=None,
                    help="Target setup type for the update of dependencies")
    p2.add_argument("--btc",
                    action='store_true',
                    default=False,
                    help="Update bitcoin-satellite")
    p2.set_defaults(func=run, update=True)

    p3 = subsubp.add_parser('tbs-drivers',
                            description="Install TBS USB receiver drivers",
                            help='Install TBS USB receiver drivers')
    p3.set_defaults(func=drivers)

    return p

def run(args):
    """Run installations"""

    # Interactive installation? I.e., requires user to press "y/n"
    interactive = (not args.yes)

    # Configure the subprocess runner
    runner.set_dry(args.dry_run)

    if (args.target is not None):
        target = target_map[args.target]
    else:
        info = config.read_cfg_file(args.cfg, args.cfg_dir)
        if (info is None):
            return
        target = info['setup']['type']

    _check_distro(target)

    if (os.geteuid() != 0 and not args.dry_run):
        util.fill_print("Some commands require root access and may prompt "
                        "for a password")

    if (args.dry_run):
        util.print_header("Dry Run Mode")

    # Update package index
    _update_pkg_repo(interactive)

    # Common dependencies (regardless of setup)
    _install_common(interactive=interactive, update=args.update, btc=args.btc)

    # Install setup-specific dependencies
    _install_specific(target, interactive=interactive, update=args.update)

def drivers(args):
    # Interactive installation? I.e., requires user to press "y/n"
    interactive = (not args.yes)

    # Configure the subprocess runner
    runner.set_dry(args.dry_run)

    if (os.geteuid() != 0 and not args.dry_run):
        util.fill_print("Some commands require root access and may prompt "
                        "for a password")

    if (args.dry_run):
        util.print_header("Dry Run Mode")

    # Install pre-requisites
    linux_release = platform.release()
    linux_headers = "linux-headers-" + linux_release
    kernel_devel = "kernel-devel-" + linux_release
    kernel_headers = "kernel-headers-" + linux_release
    apt_pkg_list = [
        "make", "gcc", "git", "patch", "patchutils",
        "libproc-processtable-perl", linux_headers
    ]
    dnf_pkg_list = [
        "make", "gcc", "git", "patch", "patchutils", "perl-Proc-ProcessTable",
        "perl-Digest-SHA", "perl-File-Copy-Recursive", kernel_devel,
        kernel_headers
    ]
    yum_pkg_list = dnf_pkg_list

    # On dnf, not always the kernel-devel/headers package will be available for
    # the same version as the current kernel. Check:
    dnf_update_required = False
    if (which("dnf")):
        res_d = runner.run(["dnf", "list", kernel_devel],
                           stdout=subprocess.DEVNULL,
                           stderr=subprocess.DEVNULL,
                           nocheck=True)
        res_h = runner.run(["dnf", "list", kernel_headers],
                           stdout=subprocess.DEVNULL,
                           stderr=subprocess.DEVNULL,
                           nocheck=True)
        if (not args.dry_run):
            kernel_devel_unavailable = (res_d.returncode != 0)
            kernel_headers_unavailable = (res_h.returncode != 0)
            dnf_update_required = kernel_devel_unavailable or \
                kernel_headers_unavailable
            if (kernel_devel_unavailable):
                print("Could not find package {}".format(kernel_devel))
            if (kernel_headers_unavailable):
                print("Could not find package {}".format(kernel_headers))

    # If the target kernel-devel/kernel-headers versions are not available for
    # the current release, suggest to run dnf update first. If no update is
    # available, and kernel-headers is the one that has a version mismatch (it
    # typically is), try with the version that is available.
    if (dnf_update_required):
        res = runner.run(["dnf", "list", "--upgrades", "kernel"],
                         stdout=subprocess.DEVNULL,
                         stderr=subprocess.DEVNULL,
                         nocheck=True)
        kernel_update_available = (res.returncode == 0)
        if (kernel_update_available):
            print("Kernel update required")
            if (util.ask_yes_or_no("OK to run \"dnf update\"?")):
                cmd = ["dnf", "update"]
                if (not interactive):
                    cmd.append("-y")
                runner.run(cmd, root=True)
                print("Please reboot to load the new kernel and try the " +
                      "command below again:")
                print("\n  blocksat-cli deps tbs-drivers\n")
                sys.exit(1)
                return

        # Fall back to the default kernel-headers package
        if kernel_headers_unavailable:
            print("Trying with the default kernel-headers package")
            dnf_pkg_list.remove(kernel_headers)
            dnf_pkg_list.append("kernel-headers")
        else:
            # Break if kernel-devel is missing for the current version and
            # there is no upgrade available.
            logger.error("Could not find an available kernel update")
            sys.exit(1)
            return

    _update_pkg_repo(interactive)
    _install_packages(apt_pkg_list,
                      dnf_pkg_list,
                      yum_pkg_list,
                      interactive=interactive,
                      update=False)

    # Clone the driver repositories
    driver_src_dir = os.path.join(args.cfg_dir, "src", "tbsdriver")
    media_build_dir = os.path.join(driver_src_dir, "media_build")
    media_dir = os.path.join(driver_src_dir, "media")

    if not os.path.exists(driver_src_dir):
        os.makedirs(driver_src_dir)

    if os.path.exists(media_build_dir):
        runner.run(["git", "pull", "origin", "master"], cwd=media_build_dir)
    else:
        runner.run(
            ["git", "clone", "https://github.com/tbsdtv/media_build.git"],
            cwd=driver_src_dir)

    if os.path.exists(media_dir):
        runner.run(["git", "pull", "origin", "latest"], cwd=media_dir)
    else:
        runner.run([
            "git", "clone", "--depth=1",
            "https://github.com/tbsdtv/linux_media.git", "-b", "latest",
            "./media"
        ],
                   cwd=driver_src_dir)

    # Build the media drivers
    nproc = int(subprocess.check_output(["nproc"]).decode().rstrip())
    nproc_arg = "-j" + str(nproc)

    runner.run(["make", "cleanall"], cwd=media_build_dir)
    runner.run(["make", "dir", "DIR=../media"], cwd=media_build_dir)
    runner.run(["make", "allyesconfig"], cwd=media_build_dir)

    # FIXME: Temporary workaround for error "modpost: "__devm_regmap_init_sccb"
    # ov9650.ko undefined!": disable ov9650 from the build. The problem was
    # observed on kernel versions 5.3.7 and 5.7.7. Apply the workaround for any
    # version < 5.8.
    if (distro.id() == "fedora"
            and LooseVersion(linux_release) < LooseVersion('5.8')):
        runner.run([
            "sed", "-i", "s/CONFIG_VIDEO_OV9650=m/CONFIG_VIDEO_OV9650=n/g",
            "v4l/.config"
        ],
                   cwd=media_build_dir)

    runner.run(["make", nproc_arg], cwd=media_build_dir)

    # Delete the previous Media Tree installation
    media_lib_path = "/lib/modules/" + linux_release + \
                     "/kernel/drivers/media/"
    runner.run(["rm", "-rf", media_lib_path], root=True, cwd=media_build_dir)

    # Install the new Media Tree
    runner.run(["make", "install"], root=True, cwd=media_build_dir)

    # Download the firmware
    tbs_linux_url = "https://www.tbsdtv.com/download/document/linux/"
    fw_tarball = "tbs-tuner-firmwares_v1.0.tar.bz2"
    fw_url = tbs_linux_url + fw_tarball
    util.download_file(fw_url, driver_src_dir, args.dry_run, logger=logger)

    # Install the firmware
    runner.run(["tar", "jxvf", fw_tarball, "-C", "/lib/firmware/"],
               root=True,
               cwd=driver_src_dir)

    if (not args.dry_run):
        print("Installation completed successfully. Please reboot now.")

def check_apps(apps):
    """Check if required apps are installed"""
    for app in apps:
        if (not which(app)):
            logging.error("Couldn't find {}. Is it installed?".format(app))
            print("\nTo install software dependencies, run:")
            print("""
            blocksat-cli deps install
            """)
            return False
    # All apps are installed
    return True


from builtins import set
from datasets import load_dataset
from collections import Counter
import sys

def static_vars(**kwargs):
    def decorate(func):
        for k in kwargs:
            setattr(func, k, kwargs[k])
        return func
    return decorate

@static_vars(dataset=load_dataset("ChristophSchuhmann/MS_COCO_2017_URL_TEXT"))
def find_image(sentence):
    urls = find_image.dataset["train"]["URL"]
    texts = find_image.dataset["train"]["TEXT"]

    results = []
    for idx, i in enumerate(texts):
        if sentence in i.lower():
            results.append(urls[idx])
    return results

def find_image_multi(sentences):
    urls = find_image.dataset["train"]["URL"]
    texts = find_image.dataset["train"]["TEXT"]
    sentence = sentences[0]
    results = Counter(find_image(sentence))
    for sentence_index in range(1,len(sentences)):
        results += Counter(find_image(sentences[sentence_index]))
    return(results.most_common(1)[0][0])

def build_caption_to_image_dict(dataset_folder):
    import json
    captions = json.load(open(f"{dataset_folder}/annotations/captions_train2017.json"))
    id2filename = dict()
    for image in captions["images"]:
        id2filename[image["id"]] = f'{dataset_folder}/train2017/{image["file_name"]}'
    ret = {i['caption'].lower(): id2filename[i['image_id']] for i in captions["annotations"]}
    captions = json.load(open(f"{dataset_folder}/annotations/captions_val2017.json"))
    id2filename = dict()
    for image in captions["images"]:
        id2filename[image["id"]] = f'{dataset_folder}/val2017/{image["file_name"]}'
    ret.update({i['caption'].lower(): id2filename[i['image_id']] for i in captions["annotations"]})
    return ret

if __name__ == "__main__":
    if len(sys.argv) > 1:
        sentences = [sys.argv[1]]
    else:
        sentences = ['a very clean white bathroom with blue', 'a bathroom vanity mirror above a bathroom', 'bathroom sink with mirror , lighting ,', 'a bathroom sink area is lit up', 'a bathroom with a white counter top']

    print(find_image_multi(sentences))


from flask import Blueprint, request, jsonify
from models.Reading import *
import json

api = Blueprint('api', __name__)

# RESTful API
#   The following RESTful API endpoints allow for the manipulation of reading entries in the database.
#
#   Requests for all endpoints except GET (which takes plant id as a parameter in the GET request)
#   should have json as the body content in the following format:
#   { "id": <only required if updating existing reading>,
#       "mac_address": <the mac address of the device you are making the reading for>,
#       "temperature": <ambient temperature percentage>,
#       "humidity": <ambient humidity percentage>,
#       "soil_moisture": <volumetric soil moisture %>,
#       "light_intensity": <light intensity of reading in lux>
#   }

# GET existing reading in the database.
@api.route('/api/readings', methods=['GET'])
def get_readings():
    id = request.args.get('id')
    num_readings = request.args.get('num_readings')
    return jsonify([i.serialize for i in Reading.query.filter_by(plant_id=id).order_by(Reading.time.desc()).limit(num_readings).all()])

# POST or write a new reading to the database.
@api.route('/api/readings', methods=['POST'])
def add_reading():
    response = {"errors": None}
    reading = json.loads(request.data)
    try:
        mac_addr = reading["mac_address"]
    except KeyError:
        response["errors"] = "Please provide a valid mac address."
    print(reading)
    temperature = float(reading["temperature"])
    humidity = float(reading["humidity"])
    light_intensity = float(reading["light_intensity"])
    soil_moisture = float(reading["soil_moisture"])
    if mac_addr is not None:
        plant = Plant.query.filter_by(mac_address=mac_addr).first()
        reading = Reading(time=time.time(), plant_id=plant.id, temperature=temperature, humidity=humidity,
                          light_intensity=light_intensity, soil_moisture=soil_moisture, mac_address=mac_addr, moisture_index=None)
        db.session.add(reading)
        db.session.commit()
    return response

# PATCH or update an existing reading in the database.
@api.route('/api/readings', methods=['PATCH'])
def update_reading():
    response = {"errors": None}
    reading = json.loads(request.data)
    existing_record = Reading.query.filter_by(id=reading["id"]).one()
    if reading["temperature"]:
        existing_record.temperature = float(reading["temperature"])
    if reading["humidity"]:
        existing_record.humidity = float(reading["humidity"])
    if reading["light_intensity"]:
        existing_record.light_intensity = float(reading["light_intensity"])
    if reading["soil_moisture"]:
        existing_record.soil_moisture = float(reading["soil_moisture"])
    db.session.commit()
    return response

# DELETE or remove an existing reading from the database.
@api.route('/api/readings', methods=['DELETE'])
def delete_reading():
    response = {"errors": None}
    reading_id = request.args.get("reading_id")
    reading = Reading.query.filter_by(id=float(reading_id)).one()
    db.session.delete(reading)
    db.session.commit()
    return response


import tkinter as tk
from PIL import Image, ImageTk
from itertools import count
import os.path

class ImageLabel(tk.Label):
    """a label that displays images, and plays them if they are gifs"""
    def load(self, im):
        if isinstance(im, str):
            im = Image.open(im)
        self.loc = 0
        self.frames = []

        try:
            for i in count(1):
                self.frames.append(ImageTk.PhotoImage(im.copy()))
                im.seek(i)
        except EOFError:
            pass

        try:
            self.delay = im.info['duration']
        except:
            self.delay = 100

        if len(self.frames) == 1:
            self.config(image=self.frames[0])
        else:
            self.next_frame()

    def unload(self):
        self.config(image="")
        self.frames = None

    def next_frame(self):
        if self.frames:
            self.loc += 1
            self.loc %= len(self.frames)
            self.config(image=self.frames[self.loc])
            self.after(self.delay, self.next_frame)

#Function to launch and run the file for the question bank
def questionBank():
    root.destroy() #close existing gui before opening next
    os.chdir("../question_bank/")
    os.system("python question_driver.py")

#Function to launch and run the file for the game board
def openGame():
    root.destroy() #close existing gui before opening next
    os.chdir("../gameplay/")
    os.system("python game_board.py")

#Test GUI for functionality to show gif
root = tk.Tk()
root.title("Welcome to Trivial Purfuit!") #set title of window
lbl = ImageLabel(root) #create image label in the gui window
__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
lbl.pack()
lbl.load('trivialpurfuit.gif') #load gif into gui window

#Create a button that links to method for running question bank
bank_button = tk.Button(root)
bank_button.config(text="Launch the Question Bank", command=questionBank)
bank_button.pack(side="left")

#Create a button that links to method for running game board
game_button = tk.Button(root)
game_button.config(text="Launch the Game Board", command=openGame)
game_button.pack(side="right")

#run mainloop to launch gui
root.mainloop()


audio_script = """ ffmpeg -f alsa  `# * Use alsa [6]` \
        -channels 4 `# Channel numbers are 4 for ps eyes` \
        -i hw:1,0 `# * Audio input [6]` \
        -t 65 audio/%d/%d/%d/%d.wav """
import os, datetime
while 1:
    time = datetime.datetime.now()
    os.system("mkdir audio/%d/%d/%d -p" % (time.month, time.day, time.hour))
    os.system("mkdir audio/%d/%d/%d -p" % (time.month, time.day, time.hour + 1)) # In case it is right on the time between hours
    os.system(audio_script % (time.month, time.day, time.hour, time.minute))


# encoding=utf-8

from datetime import datetime as date
import traceback
import domutil
import storage
from BeautifulSoup import BeautifulSoup as Soup

def extract(pages):
    products = {}

    for page in pages:
        root = Soup(page)

        elem = root.find(name='table', attrs={'id':'rsTable'})
        if not elem:
            continue

        for tr in elem.findAll(name='tr'):
            tds = tr.findAll(name='td')
            if not tds or len(tds) == 0:
                continue

            company = tds[5].getText()
            fcc_id = tds[11].getText()
            lastupdate = extract_date(tds[13].getText())

            if fcc_id in products:
                entity = products[fcc_id]
                if entity.lastupdate < lastupdate:
                    entity.lastupdate = lastupdate
            else:
                entity = storage.create_instance(storage.FCCENTITY, \
                                                 company=company, \
                                                 fcc_id=fcc_id, \
                                                 lastupdate=lastupdate)

                products[fcc_id] = entity

    storage.db.put(products.values())

def extract_date(date_str):
    values = date_str.split('/')
    for ind in xrange(0, len(values)):
        values[ind] = int(values[ind])

    mon, day, year = values
    return date(year, mon, day)


from content_editor.models import Region, create_plugin_base
from django.db import models
from django.utils.translation import gettext_lazy as _
from feincms3.applications import ApplicationType, PageTypeMixin, TemplateType
from feincms3.mixins import LanguageMixin, MenuMixin
from feincms3.pages import AbstractPage
from feincms3.plugins import image, richtext

class Page(
    AbstractPage,
    PageTypeMixin,  # For adding the articles app to pages through the CMS.
    MenuMixin,  # We have a main and a footer navigation (meta).
    LanguageMixin,  # We're building a multilingual CMS. (Also,
    # feincms3.applications depends on LanguageMixin
    # currently.)
):
    TYPES = [
        TemplateType(
            key="standard",
            title=_("standard"),
            template_name="pages/standard.html",
            regions=(Region(key="main", title=_("Main")),),
        ),
        TemplateType(
            key="with-sidebar",
            title=_("with sidebar"),
            template_name="pages/with-sidebar.html",
            regions=(
                Region(key="main", title=_("Main")),
                Region(key="sidebar", title=_("Sidebar")),
            ),
        ),
        ApplicationType(
            key="publications",
            title=_("publications"),
            urlconf="app.articles.urls",
            regions=(Region(key="main", title=_("Main")),),
        ),
        ApplicationType(
            key="blog",
            title=_("blog"),
            urlconf="app.articles.urls",
            regions=(Region(key="main", title=_("Main")),),
        ),
    ]

    # MenuMixin
    MENUS = [
        ("main", _("main")),
        ("footer", _("footer")),
    ]

PagePlugin = create_plugin_base(Page)

class RichText(richtext.RichText, PagePlugin):
    pass

class Image(image.Image, PagePlugin):
    caption = models.CharField(
        _("caption"),
        max_length=200,
        blank=True,
    )


import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.path as path
import os
import codecs

def get_endpoint(x):

    return max(x), min(x)

def postprocessing(info):

    info = sorted(info.items(), key = lambda x:x[0])
    x = [xx[0] for xx in info]
    y = [yy[1] for yy in info]

    return x, y

def draw_his(dic, des):

    for phn in dic:
        info = dic[phn]
        x, y = postprocessing(info)
        x_max, x_min = get_endpoint(x)
        y_max, y_min = get_endpoint(y)

        fig, ax = plt.subplots()
        left = np.array(x)
        right = np.array(x)
        bottom = np.zeros(len(left))
        top = bottom + y

        XY = np.array([[left, left, right, right], [bottom, top, top, bottom]]).T
        barpath = path.Path.make_compound_path_from_polys(XY)
        patch = patches.PathPatch(barpath)
        ax.add_patch(patch)

        ax.set_xlim(x[0], x[-1])
        ax.set_ylim(bottom.min(), top.max())
        plt.xlabel("Duration distribution of phone: {}".format(phn))
        plt.ylabel("Count")
        #plt.show()

        fig.savefig("{}/{}.png".format(des, phn))
        plt.close("all")

def statistics_phone(data):

    dic = {}

    for line in data:
        line = line.strip().split()
        phn = line[-1]
        dur = int((float(line[-2]) - float(line[-3])) * 1000)
        if phn not in dic:
            dic[phn] = {}
        section = dur/5
        if section not in dic[phn]:
            dic[phn][section] = 0
        dic[phn][section] += 1

    return dic

def draw(data, des):

    dic = statistics_phone(data)

    draw_his(dic, des)

def load(f):

    fp = codecs.open(f, "r", encoding = "utf8")
    data = fp.readlines()
    fp.close()

    return data

def main(ori, des):

    for f in os.listdir(ori):
        fp = ori + os.sep + f
        data = load(fp)
        draw(data, des)

if __name__ == "__main__":

    if len(sys.argv) != 3:
        print ("This script is for data statistics")
        print ("Usage: {} data info".format(sys.argv[0]))
        exit(1)

    main(sys.argv[1], sys.argv[2])


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

dataset = pd.read_csv(f"combined/emg_fsr_data.csv",header=0)
emg_fsr_time = [x for x in range(len(dataset))]
ffls_data = dataset.iloc[:,21:]
# print(ffls_data.head())
# print(ffls_data.max())
# print(ffls_data.min())

def _normalize(data):
    return (data-data.min())/(data.max()-data.min())

def _standardize(array):
  mean = np.mean(array, axis=0)
  std = np.std(array, axis=0)
  array = (array - mean) / std
  return array

def press_locations(ffls_data,finger):
  ffls_data.columns=['Fing1','Fing2','Fing3','Fing4','Fing5','Fing6'] # Renaming Columns
  ffls_standardized = _standardize(ffls_data)
  ffls_normalized = _normalize(ffls_standardized)

  # Copying Key column to FFLS Data
  ffls_normalized['key']=dataset.iloc[:,0]
  # Reordering columns
  ffls_normalized=ffls_normalized[['key','Fing1','Fing2','Fing3','Fing4','Fing5','Fing6']]
  # Calculate Thresholds
  thresholds = [x-0.03 for x in ffls_normalized.mean()]

  indices=[]
  # https://stackoverflow.com/questions/21800169/python-pandas-get-index-of-rows-where-column-matches-certain-value
  # Find where Finger is pressed
  idx_list=np.where(ffls_normalized[f'Fing{finger}']<thresholds[finger])[0]
  for idx in idx_list:
    indices.append(idx)
  return indices

mov_avg_ffls = ffls_data.rolling(3).mean()
mov_avg_ffls.fillna(0.0,inplace=True)

# thresholds = [x-0.03 for x in ffls_normalized.mean()]
# plt.plot(emg_fsr_time,ffls_normalized)
# for i in range(6):
#     plt.subplot(3,2,i+1)
#     plt.axhline(y=thresholds[i], color='r', linestyle='-')
#     plt.plot(emg_fsr_time,ffls_normalized.iloc[:,i])
    # plt.hist(ffls_normalized.iloc[:,i],50)

plt.plot(emg_fsr_time,mov_avg_ffls)
colors=['b', 'g', 'r', 'c', 'm', 'y', 'k', 'w']
for i in range(1,7):
  for idx in press_locations(mov_avg_ffls,i):
    plt.axvline(x=idx,linestyle='-',color=colors[i])
plt.show()


import decimal

from django.core.management.base import BaseCommand

from storeapp.models import Product

class Command(BaseCommand):
    help = 'Update the price of product by id'

    def add_arguments(self, parser):
        parser.add_argument('pk', type=int, help="Product's id")
        parser.add_argument('price', type=decimal.Decimal, help="Price of the product")

    def handle(self, *args, **kwargs):
        pk = kwargs.get('pk')
        price = kwargs.get('price')
        product = Product.objects.filter(pk=pk).first()
        if product:
            product.price = price
            product.save()
        self.stdout.write(f'{product}')


import logging
from contextlib import contextmanager
from os.path import exists
from sqlite3 import connect, Cursor

from constants import NUM_CAMERAS, NUM_BUTTONS, DB_FILE

LOG = logging.getLogger("db")

class Database:
    connection = None

    def __init__(self):
        if not exists(DB_FILE):
            LOG.info("Initialize sqlite database...")
            with self.cursor() as cur:
                cur.execute("CREATE TABLE positions ("
                            "cam INTEGER NOT NULL, "
                            "pos INTEGER NOT NULL, "
                            "name VARCHAR NOT NULL DEFAULT '', "
                            "btn_class VARCHAR NOT NULL DEFAULT 'btn-secondary', "
                            "focus INTEGER NOT NULL DEFAULT -1, "
                            "PRIMARY KEY (cam, pos))")
                for cam in range(NUM_CAMERAS):
                    for pos in range(NUM_BUTTONS):
                        cur.execute("INSERT INTO positions (cam, pos) VALUES (?, ?)", (cam, pos))
            LOG.info("Initialization complete")

    @contextmanager
    def dict_cursor(self) -> Cursor:
        connection = None
        try:
            connection = connect(DB_FILE)

            def dict_factory(cursor, row):
                return {c[0]: row[i] for i, c in enumerate(cursor.description)}

            connection.row_factory = dict_factory
            yield connection.cursor()
            connection.commit()
        finally:
            if connection is not None:
                connection.close()

    @contextmanager
    def cursor(self) -> Cursor:
        connection = None
        try:
            connection = connect(DB_FILE)
            yield connection.cursor()
            connection.commit()
        finally:
            if connection is not None:
                connection.close()

    def set_button(self, cam: int, pos: int, name: str, btn_class: str):
        with self.cursor() as cur:
            cur.execute("UPDATE positions SET name = ?, btn_class = ? WHERE cam = ? AND pos = ?",
                        (name, btn_class, cam, pos))

    def set_focus(self, cam: int, pos: int, focus: int):
        with self.cursor() as cur:
            cur.execute("UPDATE positions SET focus = ? WHERE cam = ? AND pos = ?", (focus, cam, pos))

    def get_focus(self, cam: int, pos: int) -> int:
        with self.cursor() as cur:
            cur.execute("SELECT focus FROM positions WHERE cam = ? AND pos = ?", (cam, pos))
            return cur.fetchone()[0]

    def get_data(self) -> list:
        with self.dict_cursor() as cur:
            return list(cur.execute("SELECT cam, pos, name, btn_class FROM positions ORDER BY cam, pos"))

    def clear_buttons(self):
        with self.cursor() as cur:
            cur.execute("UPDATE positions SET name = '', btn_class = 'btn-secondary'")


#!/usr/bin/env python3
import time
from geventwebsocket import WebSocketServer, WebSocketApplication, Resource
from collections import OrderedDict
from flask import Flask, send_file
from werkzeug.debug import DebuggedApplication
app = Flask(__name__, static_folder="../frontend", static_url_path="")
app.debug = True
class EchoApplication(WebSocketApplication):

    def on_open(self):
        print("Connection opened")

    def on_message(self, message):
        print(message)
        self.ws.send(message)

    def on_close(self, reason):
        print(reason)

@app.route("/")
def startpage():
    return send_file('../frontend/index.html')

WebSocketServer(
    ('0.0.0.0', 5000),
    Resource(OrderedDict([('^/test', EchoApplication),
                    ("^/.*", DebuggedApplication(app))]))
    , debug=True
).serve_forever()


import numpy as np
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

#빙어와 도미가 합쳐진 데이터들
fish_length = [25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7,
31.0, 31.0, 31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5,
34.0, 34.0, 34.5, 35.0, 35.0, 35.0, 35.0, 36.0, 36.0, 37.0,
38.5, 38.5, 39.5, 41.0, 41.0, #여기 까지가 35개
9.8,10.5, 10.6, 11.0, 11.2, 11.3, 11.8, 11.8, 12.0, 12.2, 12.4, 13.0, 14.3, 15.0]

fish_weight = [242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0, 450.0, 500.0,
475.0, 500.0, 500.0, 340.0, 600.0, 600.0, 700.0, 700.0, 610.0, 650.0,
575.0, 685.0, 620.0, 680.0, 700.0, 725.0, 720.0, 714.0, 850.0, 1000.0,
920.0, 955.0, 925.0, 975.0, 950.0, #여기 까지가 35개
6.7, 7.5, 7.0, 9.7, 9.8, 8.7, 10.0, 9.9, 9.8, 12.2, 13.4, 12.2, 19.7, 19.9]

#도미와 빙어 데이터를 합친다.
fish_data = [[l, w] for l, w in zip(fish_length, fish_weight)]
fish_target = [1]*35 + [0]*14 #도미 35마리 #빙어 14마리
# 훈련 데이터 입출력 35개
train_input = fish_data[:35]
train_target = fish_target[:35]
# 시험 데이터 입출력 35개
test_input = fish_data[35:]
test_target = fish_target[35:]

#주어진 데이터로 학습 시작
kn = KNeighborsClassifier()
KN = kn.fit(train_input, train_target)

#학습 결과 값 출력
print(kn.score(test_input,test_target))

#훈련데이터를 섞어주기위해기존 1차원 배열(List)을 다차원 배열(Array)로 바꿈
input_arr = np.array(fish_data)
target_arr = np.array(fish_target)

input(input_arr)

#데이터를 섞어줌

np.random.seed(42) #왜 42지?
index = np.arrange(49)
np.random.shuffle(index)

np.random.shuffle(index) #내가 볼려고 프린트한거
#index를 랜덤으로 생성하여 도미빙어가 구분되어있는 데이터를 섞어줌
train_input = input_arr[index[:35]]
train_target = target_arr[index[:35]]

test_input = input_arr[index[35:]]
test_target = target_arr[index[35:]]
#데이터를 나누고 확인하기 잘섞여있는지 그래프로 알 수 있음

plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(train_input[:,0], train_input[:,1])
plt.xlabel('length')
plt.xlabel('weight')
plt.show()

kn = kn.fit(train_input, train_target)
#훈련세트와 데이터세트의 유사성 나옴 1 에 가까울 수 록 유사함 but 항상 1이 나오진 않음
print(kn.score(test_input, test_target))

#넘파이로 데이터 준비 #Data_Size x [길이,무게] // Data_Size 행 x 2 열
fish_data = np.column_stack((fish_length, fish_weight))

fish_target = np.concatenate(np.ones(35),np.zeros(14))

train_input, test_input, train_target, test_target = train_test_split(
    fish_data,fish_target,stratify=fish_target, random_state=42) # shuffle seed = 42

#사이킷런으로 데이터를 나눈다.
kn.KNeighborsClassifier()
kn.fit(train_input,train_target)
print(kn.score(test_input,test_target))
#특이한 도미를 추가한다.
print(kn.predict([25,100]))

#[25,150]을 판단하는데 사용된 점들의 index와 그들간의 거리 distancs
distances, index = kn.kneighbors([[25,150]])

plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25,150,marker="^")
plt.scatter(train_input[index,0],
train_input[index,1],marker ='D')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

#기준을 맞춰준다.

plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25,150,marker="^")
plt.scatter(train_input[index,0],
train_input[index,1],marker ='D')
plt.xlim((0,1000))
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

#표준점수로 변경
mean = np.mean(train_input,axis=0)
std = np.std(train_input, axis=0)

print(mean, std)

train_scaled = (train_input - mean) / std
#수상힌 도미 다시 표시하기
new = ([25,150] - mean) / std

plt.scatter(train_scaled[:0], train_scaled[:,1])
plt.scatter(new[0], new[1], marker = "^")
plt.xlabel("length")
plt.ylabel("weight")
plt.show()

#전처리 데이터에서 모델 훈련
kn.fit(train_scaled,train_target)
test_scaled = (test_input - mean) / std
kn.score(test_scaled, test_target)

print("새로운 생선" + kn.predict([new]))

distances, indexes = kn.kneighbors(new)
plt.scatter(train_scaled[:0], train_scaled[:,1])
plt.scatter(new[0], new[1], marker = "^")
plt.scatter(train_scaled[indexes,0],train_scaled[indexes,1],marker='D')
plt.xlabel("length")
plt.ylabel("weight")
plt.show()


from django.db import models
from django.utils.timezone import now as timezone_now

# Create your models here.

class AREA_PROFESIONAL(models.Model):

    nombre_area = models.CharField(
        max_length = 100,
        primary_key = True
    )

class PROFESIONAL(models.Model):

    identificador_medico = models.IntegerField(
        null= False,
        default = -1,
        primary_key= True
    )
    nombre = models.CharField(
        null = False,
        default= '',
        max_length= 150
    )

    apellido1 = models.CharField(
        null = False,
        default='',
        max_length= 150
    )

    apellido2 = models.CharField(
        blank=True,
        default = '',
        max_length= 150
    )
    edad = models.IntegerField(
        null = False
    )

    area = models.ForeignKey(
       to = AREA_PROFESIONAL,
       null = False,
       on_delete = models.CASCADE
    )

    def __str__(self):
        return "Soy el profesional" + self.pk

class RECURSO(models.Model):

    nombre_recurso = models.CharField(
        null = False,
        max_length= 50
    )
    tipo = models.CharField(
        null = False,
        max_length= 50
    )

    def __str__(self):
        return "Soy el recurso" + self.pk

class ACTIVIDAD(models.Model):
    nombre_actividad = models.CharField(
        null = False,
        max_length = 50
    )
    tipo = models.ForeignKey(
        to=RECURSO,
        null=False,
        on_delete=models.CASCADE,
        verbose_name='tipo de recuerso profesional asociado a una actividad'
    )
    def __str(self):
        return "Soy la actividad" + self.pk

class PROCEDIMIENTOS(models.Model):
    nombre_procedimiento = models.CharField(
        null = False,
        max_length = 100
    )
class PACIENTE(models.Model):
    nombre = models.CharField(
        null=False,
        default='',
        max_length = 150
    )
    apellido1 = models.CharField(
        null=False,
        default = '',
        max_length = 150
    )
    apellido2 = models.CharField(
        blank=True,
        default= '',
        max_length=150
    )

    edad = models.IntegerField(
        null = False
    )
    fecha_inclusion = models.DateField(
        default=timezone_now
    )
    diagnostico = models.CharField(
        null = False,
        max_length= 150
    )
    procedimiento_paciente = models.ForeignKey(
        to = PROCEDIMIENTOS,
        null=False,
        on_delete=models.CASCADE,
        verbose_name='tipo de procedimiento asociado a un paciente'
    )
    actividad_paciente = models.ForeignKey(
        to = ACTIVIDAD,
        null=False,
        on_delete=models.CASCADE,
        verbose_name='Actividad asociada a un paciente'
    )
    profesional_paciente = models.ForeignKey(
        to = PROFESIONAL,
        null=False,
        on_delete=models.CASCADE,
        verbose_name='Profesional asociado a un paciente'
    )

    def __str__(self):
        return "Soy el paciente" + self.pk


#!/usr/bin/env python
# coding: utf-8

# In[6]:

import pyttsx3

# In[9]:

import PyPDF2

# In[13]:

hikaye = open("deep-learning.pdf","rb")

# In[12]:

pdfOkuyucu = PyPDF2.PdfFileReader(hikaye)

# In[14]:

from PyPDF2 import PdfReader

# In[16]:

pdfOkuyucu = PyPDF2.PdfReader(hikaye)

# In[18]:

engine = pyttsx3.init()

# In[19]:

voices = engine.getProperty('voices')

# In[20]:

engine.setProperty('voice',voices[0].id)

# In[31]:

for sayfa_numarasi in range(0,len(pdfOkuyucu.pages)):
    sayfa = pdfOkuyucu.pages[sayfa_numarasi]
    yazi = sayfa.extract_text
    engine.say(yazi)
    engine.runAndWait()

# In[ ]:


from django.urls import path
from . import views

app_name = "cats"
urlpatterns = [
    #? showing all objects
    path("", name="all",view=views.CatView.as_view()),
    path("breed/",name="breed_list",view=views.BreedView.as_view()),
    #? CRUD cats
    path("cat/create/", name="cat_create",view=views.CatCreate.as_view()),
    path("cat/<int:pk>/update", name="cat_update",view=views.CatUpdate.as_view()),
    path("cat/<int:pk>/delete", name="cat_delete",view=views.CatDelete.as_view()),
    #? CRUD breeds
    path("breed/create/", name="breed_create",view=views.BreedCreate.as_view()),
    path("breed/<int:pk>/update", name="breed_update",view=views.BreedUpdate.as_view()),
    path("breed/<int:pk>/delete", name="breed_delete",view=views.BreedDelete.as_view()),
]


# Lawrence McAfee

# ~~~~~~~~ import ~~~~~~~~
from modules.node.HierNode import HierNode
from modules.node.LeafNode import LeafNode
from modules.node.Stage import Stage
from modules.node.block.CodeBlock import CodeBlock as cbk
from modules.node.block.ImageBlock import ImageBlock as ibk
from modules.node.block.MarkdownBlock import MarkdownBlock as mbk

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
blocks = [
    mbk("In regression trees, the recursive binary splitting technique is used to divide a particular feature in the dataset into two regions. The splitting is carried out by choosing a value of the feature that minimizes the regression error measure. This step is done for all the predictors in the dataset by finding a value that reduces the squared error of the final tree. This process is repeated continuously for every sub-tree or sub-region until a stopping criterion is reached. For example, we can stop the algorithm when no region contains less than ten observations. An example of a tree resulting from the splitting of a feature space into six regions is shown in Figure 23-2."),
    ibk(None, "Figure 23-2. Left: An example of splitting a 2-D dataset into sub-trees/regions using the recursive binary splitting technique. Right: The resulting tree from the partitioning on the left."),
]

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class Content(LeafNode):
    def __init__(self):
        super().__init__(
            "Growing a Regression Tree",
            Stage.REMOVE_EXTRANEOUS,
            Stage.ORIG_BLOCKS,
            # Stage.CUSTOM_BLOCKS,
            # Stage.ORIG_FIGURES,
            # Stage.CUSTOM_FIGURES,
            # Stage.CUSTOM_EXERCISES,
        )
        self.add(mbk("# Growing a Regression Tree"))
        [self.add(a) for a in blocks]

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class Growinga(HierNode):
    def __init__(self):
        super().__init__("Growing a Regression Tree")
        self.add(Content())

# eof


from pathlib import Path
from typing import List, Optional

import cli_ui as ui

from tsrc.executor import Outcome, Task
from tsrc.git import run_git, run_git_captured
from tsrc.repo import Remote, Repo

class RemoteSetter(Task[Repo]):
    """
    For each repository:

      * look for the remote configured in the manifest,
      * add any missing remote,
      * if a remote is found but with an incorrect URL, update its URL.

    """

    def __init__(self, workspace_path: Path) -> None:
        self.workspace_path = workspace_path

    def describe_item(self, item: Repo) -> str:
        return item.dest

    def describe_process_start(self, item: Repo) -> List[ui.Token]:
        return ["Configuring remotes", item.dest]

    def describe_process_end(self, item: Repo) -> List[ui.Token]:
        return [ui.green, "ok", ui.reset, item.dest]

    def process(self, index: int, count: int, repo: Repo) -> Outcome:
        # Note:
        #   When self.parallel is True we need to return a string describing
        #   all the changes, otherwise, we can just call cli_ui.info() directly
        summary_lines = []
        for remote in repo.remotes:
            existing_remote = self.get_remote(repo, remote.name)
            if existing_remote:
                if existing_remote.url != remote.url:
                    self.set_remote(repo, remote)
                    summary_lines.append(
                        f"{repo.dest}: remote '{remote.name}' set to '{remote.url}'"
                    )
            else:
                self.add_remote(repo, remote)
                summary_lines.append(
                    f"{repo.dest}: added remote '{remote.name}' with url: '{remote.url}'"
                )
        return Outcome.from_lines(summary_lines)

    def get_remote(self, repo: Repo, name: str) -> Optional[Remote]:
        full_path = self.workspace_path / repo.dest
        rc, url = run_git_captured(full_path, "remote", "get-url", name, check=False)
        if rc != 0:
            return None
        else:
            return Remote(name=name, url=url)

    def set_remote(self, repo: Repo, remote: Remote) -> None:
        full_path = self.workspace_path / repo.dest
        # fmt: off
        self.info_3(
            repo.dest + ":", "Update remote", ui.reset,
            ui.bold, remote.name, ui.reset,
            "to new url:", ui.brown, f"({remote.url})"
        )
        # fmt: on
        run_git(full_path, "remote", "set-url", remote.name, remote.url)

    def add_remote(self, repo: Repo, remote: Remote) -> None:
        full_path = self.workspace_path / repo.dest
        # fmt: off
        self.info_3(
            repo.dest + ":", "Add remote", ui.reset,
            ui.bold, remote.name, ui.reset, ui.brown, f"({remote.url})"
        )
        # fmt: on
        self.run_git(full_path, "remote", "add", remote.name, remote.url)