import cv2
import mediapipe as mp
import os
import Image_processing as ip

mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_face_mesh = mp.solutions.face_mesh

# For static images:
def static_images():
  IMAGE_FILES = []
  drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)
  with mp_face_mesh.FaceMesh(
      static_image_mode=True,
      max_num_faces=1,
      refine_landmarks=True,
      min_detection_confidence=0.5) as face_mesh:
    for idx, file in enumerate(IMAGE_FILES):
      image = cv2.imread(file)
      imgwidth = image.shape[0]
      imgheight = image.shape[1]
      # Convert the BGR image to RGB before processing.
      results = face_mesh.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))

      # Print and draw face mesh landmarks on the image.
      if not results.multi_face_landmarks:
        continue
      annotated_image = image.copy()
      for face_landmarks in results.multi_face_landmarks:
        face_localtion(face_landmarks.landmark, imgwidth, imgheight)
        print('face_landmarks:', face_landmarks)
        mp_drawing.draw_landmarks(
            image=annotated_image,
            landmark_list=face_landmarks,
            connections=mp_face_mesh.FACEMESH_TESSELATION,
            landmark_drawing_spec=None,
            connection_drawing_spec=mp_drawing_styles
            .get_default_face_mesh_tesselation_style())
        mp_drawing.draw_landmarks(
            image=annotated_image,
            landmark_list=face_landmarks,
            connections=mp_face_mesh.FACEMESH_CONTOURS,
            landmark_drawing_spec=None,
            connection_drawing_spec=mp_drawing_styles
            .get_default_face_mesh_contours_style())
        mp_drawing.draw_landmarks(
            image=annotated_image,
            landmark_list=face_landmarks,
            connections=mp_face_mesh.FACEMESH_IRISES,
            landmark_drawing_spec=None,
            connection_drawing_spec=mp_drawing_styles
            .get_default_face_mesh_iris_connections_style())
      cv2.imwrite('/tmp/annotated_image' + str(idx) + '.png', annotated_image)

# For webcam input:
def webcam_input():
  drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)
  cap = cv2.VideoCapture(0)
  with mp_face_mesh.FaceMesh(
      max_num_faces=1,
      refine_landmarks=True,
      min_detection_confidence=0.5,
      min_tracking_confidence=0.5) as face_mesh:
    while cap.isOpened():
      success, image = cap.read()
      if not success:
        print("Ignoring empty camera frame.")
        # If loading a video, use 'break' instead of 'continue'.
        continue

      # To improve performance, optionally mark the image as not writeable to
      # pass by reference.

      image.flags.writeable = False
      image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

      imgw = image.shape[0]
      imgh = image.shape[1]

      results = face_mesh.process(image)
      # Draw the face mesh annotations on the image.
      image.flags.writeable = True
      image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
      if results.multi_face_landmarks:
        for face_landmarks in results.multi_face_landmarks:
          #face_localtion(face_landmarks, imgw, imgh)

          mp_drawing.draw_landmarks(
              image=image,
              landmark_list=face_landmarks,
              connections=mp_face_mesh.FACEMESH_TESSELATION,
              landmark_drawing_spec=None,
              connection_drawing_spec=mp_drawing_styles
              .get_default_face_mesh_tesselation_style())
          mp_drawing.draw_landmarks(
              image=image,
              landmark_list=face_landmarks,
              connections=mp_face_mesh.FACEMESH_CONTOURS,
              landmark_drawing_spec=None,
              connection_drawing_spec=mp_drawing_styles
              .get_default_face_mesh_contours_style())
          mp_drawing.draw_landmarks(
              image=image,
              landmark_list=face_landmarks,
              connections=mp_face_mesh.FACEMESH_IRISES,
              landmark_drawing_spec=None,
              connection_drawing_spec=mp_drawing_styles
              .get_default_face_mesh_iris_connections_style())
      # Flip the image horizontally for a selfie-view display.
      cv2.imshow('MediaPipe Face Mesh', cv2.flip(image, 1))
      if cv2.waitKey(5) & 0xFF == 27:
        break
  cap.release()

def face_localtion(landmark, c=0, img_name='', path=''):

  x = landmark.location_data.relative_bounding_box.xmin
  y = landmark.location_data.relative_bounding_box.ymin
  w = landmark.location_data.relative_bounding_box.width
  h = landmark.location_data.relative_bounding_box.height
  #output = str(c) + ' ' + str(x) + ' ' + str(y) + ' ' + str(w) + ' ' + str(h)
  new_x = x + w/2
  new_y = y + h/2

  output = f'{c} {new_x} {new_y} {w} {h}'
  print(output)
  with open(path + f'/{img_name}.txt', 'w') as w:
    w.write(output)

def rac_face(show_img=0, path='/Users/leo/Downloads/images/', label_folder='/Users/leo/Downloads/labels/'):
  classification = input('Classification: ')

  img = ip.image_path(path)
  mp_face_detection = mp.solutions.face_detection   # 建立偵測方法
  mp_drawing = mp.solutions.drawing_utils           # 建立繪圖方法
  for i in img:
    print(i)
    img_name = i.split('images/')[1]
    img_file = cv2.imread(i)
    with mp_face_detection.FaceDetection(             # 開始偵測人臉
        model_selection=0, min_detection_confidence=0.5) as face_detection:

        img2 = cv2.cvtColor(img_file, cv2.COLOR_BGR2RGB)   # 將 BGR 顏色轉換成 RGB

        results = face_detection.process(img2)        # 偵測人臉

        if results.detections:
            for detection in results.detections:
              face_localtion(detection, classification, img_name, label_folder)
              mp_drawing.draw_detection(img_file, detection)  # 標記人臉
        if show_img:
          cv2.imshow('oxxostudio', img_file)
          cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__=='__main__':
  webcam_input()
  #static_images()
  # show_img 0 or 1
  # 先label再丟進train_test_valid
  #rac_face(show_img=0)


"""Make name unique for category table

Revision ID: 6cacd4db55b8
Revises: fda01d592a69
Create Date: 2020-06-21 01:46:07.605804

"""
import salsa
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '6cacd4db55b8'
down_revision = 'fda01d592a69'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_unique_constraint(op.f('uq_category_name'), 'category', ['name'])
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint(op.f('uq_category_name'), 'category', type_='unique')
    # ### end Alembic commands ###


import pygame
import os
import math
import random

#setting display
pygame.init()
WIDTH,HEIGHT = 800, 500
win = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("Hangman Game!!")

#loading images
images = []
for i in range(6):
    image = pygame.image.load("./images/hangman" + str(i)+ ".png")
    images.append(image)

# game variables
hangman_status = 0 #for the image
words = ["SNAPE", "HARRY", "ALBUS", "RON", "LILLY"]
word = random.choice(words)
guessed = []

# button variables
RADIUS = 20
GAP  = 15
letters = []
startX = round((WIDTH - (RADIUS * 2 + GAP)* 13)/2)
startY = 400
A = 65

for i in range(26):
    x = startX + GAP * 2 + ((RADIUS * 2 + GAP) * (i % 13))
    y = startY + ((i // 13) * (GAP + 2 * RADIUS))
    letters.append([x,y,chr(A+i),True])

# Fonts
LETTER_FONT = pygame.font.SysFont('consolas', 30)
WORD_FONT = pygame.font.SysFont('consolas', 40)
TITLE_FONT = pygame.font.SysFont('consolas', 50)

def draw():
    win.fill(WHITE)

    #draw title
    text = TITLE_FONT.render("HARRY POTTER HANGMAN", 1, BLACK)
    win.blit(text, (int(WIDTH/2 - text.get_width()/2), 20))

    #draw word
    displayWord = ""
    for letter in word:
        if letter in guessed:
            displayWord += letter + " "
        else:
            displayWord += "_ "
    text = WORD_FONT.render(displayWord, 1, BLACK)
    win.blit(text, (400, 200))

    # Drawing letters
    for letter in letters:
        x, y, ltr, visible = letter
        if visible == True:
            pygame.draw.circle(win, BLACK, (x,y), RADIUS, 2)
            text = LETTER_FONT.render(ltr, 1, BLACK)
            win.blit(text, (int(x - text.get_width()/2), int(y - text.get_height()/ 2)))

    if(hangman_status < 6):
        win.blit(images[hangman_status], (150,100))
    pygame.display.update()

#colors
WHITE = (255,255,255)
BLACK = (0,0,0)
#setting up a loop
FPS = 60
clock = pygame.time.Clock()

def displayMessage(message):
    pygame.time.delay(1000)
    win.fill(WHITE)
    text = WORD_FONT.render(message, 1, BLACK)
    win.blit(text, (int(WIDTH/2 - text.get_width()/2), int(HEIGHT/2 - text.get_height()/2)))
    pygame.display.update()
    pygame.time.delay(3000)

run = True

while(run == True):
    clock.tick(FPS)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            m_x, m_y = pygame.mouse.get_pos()
            for letter in letters:
                x, y, ltr, visible = letter
                if visible :
                    dis = math.sqrt((x - m_x)**2 + (y - m_y)**2)
                    if(dis < RADIUS):
                        letter[3] = False
                        guessed.append(ltr)
                        if ltr not in word:
                            hangman_status += 1

    draw()

    won = True
    for ltr in word:
        if ltr not in guessed:
            won = False
            break

    if won:
        displayMessage("YOU WON !!")
        break

    if hangman_status == 6:
        displayMessage("YOU LOST !!")
        break

pygame.quit()


from flask import Flask, request, render_template
import utils

app = Flask(__name__)

@app.route('/')
def get_all_candidates():
    candidates = utils.load_candidates()
    return render_template('list.html', candidates=candidates)

@app.route('/candidate/<int:candidate_id>')
def candidate_profile(candidate_id):
    candidate = utils.get_candidate_by_id(candidate_id)
    return render_template('card.html', candidate=candidate)

@app.route('/search/<candidate_name>')
def search_candidate(candidate_name):
    found_candidates = utils.get_candidates_by_name(candidate_name)
    number_candidates = len(found_candidates)
    return render_template('search.html', number=number_candidates, found_candidates=found_candidates)

@app.route('/skill/<skill_name>')
def search_candidate_by_skill(skill_name):
    found_candidates = utils.get_candidates_by_skill(skill_name)
    number_candidates = len(found_candidates)
    return render_template('skill.html', skill_name=skill_name,
                           number=number_candidates,
                           found_candidates=found_candidates)

if __name__ == "__main__":
    app.run()


import hashlib
import os
import types
import warnings

import numpy as np
from numpy.testing import assert_array_almost_equal as assert_arr_almost
import pytest
import shapely.geometry as sgeom

from cartopy import config
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt

#: Maps Google tile coordinates to native mercator coordinates as defined
#: by https://goo.gl/pgJi.
KNOWN_EXTENTS = {(0, 0, 0): (-20037508.342789244, 20037508.342789244,
                             -20037508.342789244, 20037508.342789244),
                 (2, 0, 2): (0., 10018754.17139462,
                             10018754.17139462, 20037508.342789244),
                 (0, 2, 2): (-20037508.342789244, -10018754.171394622,
                             -10018754.171394622, 0),
                 (2, 2, 2): (0, 10018754.17139462,
                             -10018754.171394622, 0),
                 (8, 9, 4): (0, 2504688.542848654,
                             -5009377.085697312, -2504688.542848654),
                 }

def GOOGLE_IMAGE_URL_REPLACEMENT(self, tile):
    x, y, z = tile
    return (f'https://chart.googleapis.com/chart?chst=d_text_outline&'
            f'chs=256x256&chf=bg,s,00000055&chld=FFFFFF|16|h|000000|b||||'
            f'Google:%20%20({x},{y})|Zoom%20{z}||||||'
            f'____________________________')

def test_google_tile_styles():
    """
    Tests that setting the Google Maps tile style works as expected.

    This is essentially just assures information is properly propagated through
    the class structure.
    """
    reference_url = ("https://mts0.google.com/vt/lyrs={style}@177000000&hl=en"
                     "&src=api&x=1&y=2&z=3&s=G")
    tile = ["1", "2", "3"]

    # Default is street.
    gt = cimgt.GoogleTiles()
    url = gt._image_url(tile)
    assert reference_url.format(style="m") == url

    # Street
    gt = cimgt.GoogleTiles(style="street")
    url = gt._image_url(tile)
    assert reference_url.format(style="m") == url

    # Satellite
    gt = cimgt.GoogleTiles(style="satellite")
    url = gt._image_url(tile)
    assert reference_url.format(style="s") == url

    # Terrain
    gt = cimgt.GoogleTiles(style="terrain")
    url = gt._image_url(tile)
    assert reference_url.format(style="t") == url

    # Streets only
    gt = cimgt.GoogleTiles(style="only_streets")
    url = gt._image_url(tile)
    assert reference_url.format(style="h") == url

    # Exception is raised if unknown style is passed.
    with pytest.raises(ValueError):
        cimgt.GoogleTiles(style="random_style")

def test_google_wts():
    gt = cimgt.GoogleTiles()

    ll_target_domain = sgeom.box(-15, 50, 0, 60)
    multi_poly = gt.crs.project_geometry(ll_target_domain, ccrs.PlateCarree())
    target_domain = multi_poly.geoms[0]

    with pytest.raises(AssertionError):
        list(gt.find_images(target_domain, -1))
    assert (tuple(gt.find_images(target_domain, 0)) ==
            ((0, 0, 0),))
    assert (tuple(gt.find_images(target_domain, 2)) ==
            ((1, 1, 2), (2, 1, 2)))

    assert (list(gt.subtiles((0, 0, 0))) ==
            [(0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1)])
    assert (list(gt.subtiles((1, 0, 1))) ==
            [(2, 0, 2), (2, 1, 2), (3, 0, 2), (3, 1, 2)])

    with pytest.raises(AssertionError):
        gt.tileextent((0, 1, 0))

    assert_arr_almost(gt.tileextent((0, 0, 0)), KNOWN_EXTENTS[(0, 0, 0)])
    assert_arr_almost(gt.tileextent((2, 0, 2)), KNOWN_EXTENTS[(2, 0, 2)])
    assert_arr_almost(gt.tileextent((0, 2, 2)), KNOWN_EXTENTS[(0, 2, 2)])
    assert_arr_almost(gt.tileextent((2, 2, 2)), KNOWN_EXTENTS[(2, 2, 2)])
    assert_arr_almost(gt.tileextent((8, 9, 4)), KNOWN_EXTENTS[(8, 9, 4)])

def test_tile_bbox_y0_at_south_pole():
    tms = cimgt.MapQuestOpenAerial()

    # Check the y0_at_north_pole keywords returns the appropriate bounds.
    assert_arr_almost(tms.tile_bbox(8, 6, 4, y0_at_north_pole=False),
                      np.array(KNOWN_EXTENTS[(8, 9, 4)]).reshape([2, 2]))

def test_tile_find_images():
    gt = cimgt.GoogleTiles()
    # Test the find_images method on a GoogleTiles instance.
    ll_target_domain = sgeom.box(-10, 50, 10, 60)
    multi_poly = gt.crs.project_geometry(ll_target_domain, ccrs.PlateCarree())
    target_domain = multi_poly.geoms[0]

    assert (list(gt.find_images(target_domain, 4)) ==
            [(7, 4, 4), (7, 5, 4), (8, 4, 4), (8, 5, 4)])

@pytest.mark.network
def test_image_for_domain():
    gt = cimgt.GoogleTiles()
    gt._image_url = types.MethodType(GOOGLE_IMAGE_URL_REPLACEMENT, gt)

    ll_target_domain = sgeom.box(-10, 50, 10, 60)
    multi_poly = gt.crs.project_geometry(ll_target_domain, ccrs.PlateCarree())
    target_domain = multi_poly.geoms[0]

    _, extent, _ = gt.image_for_domain(target_domain, 6)

    ll_extent = ccrs.Geodetic().transform_points(gt.crs,
                                                 np.array(extent[:2]),
                                                 np.array(extent[2:]))
    assert_arr_almost(ll_extent[:, :2],
                      [[-11.25, 48.92249926],
                       [11.25, 61.60639637]])

def test_quadtree_wts():
    qt = cimgt.QuadtreeTiles()

    ll_target_domain = sgeom.box(-15, 50, 0, 60)
    multi_poly = qt.crs.project_geometry(ll_target_domain, ccrs.PlateCarree())
    target_domain = multi_poly.geoms[0]

    with pytest.raises(ValueError):
        list(qt.find_images(target_domain, 0))

    assert qt.tms_to_quadkey((1, 1, 1)) == '1'
    assert qt.quadkey_to_tms('1') == (1, 1, 1)

    assert qt.tms_to_quadkey((8, 9, 4)) == '1220'
    assert qt.quadkey_to_tms('1220') == (8, 9, 4)

    assert tuple(qt.find_images(target_domain, 1)) == ('0', '1')
    assert tuple(qt.find_images(target_domain, 2)) == ('03', '12')

    assert list(qt.subtiles('0')) == ['00', '01', '02', '03']
    assert list(qt.subtiles('11')) == ['110', '111', '112', '113']

    with pytest.raises(ValueError):
        qt.tileextent('4')

    assert_arr_almost(qt.tileextent(''), KNOWN_EXTENTS[(0, 0, 0)])
    assert_arr_almost(qt.tileextent(qt.tms_to_quadkey((2, 0, 2), google=True)),
                      KNOWN_EXTENTS[(2, 0, 2)])
    assert_arr_almost(qt.tileextent(qt.tms_to_quadkey((0, 2, 2), google=True)),
                      KNOWN_EXTENTS[(0, 2, 2)])
    assert_arr_almost(qt.tileextent(qt.tms_to_quadkey((2, 0, 2), google=True)),
                      KNOWN_EXTENTS[(2, 0, 2)])
    assert_arr_almost(qt.tileextent(qt.tms_to_quadkey((2, 2, 2), google=True)),
                      KNOWN_EXTENTS[(2, 2, 2)])
    assert_arr_almost(qt.tileextent(qt.tms_to_quadkey((8, 9, 4), google=True)),
                      KNOWN_EXTENTS[(8, 9, 4)])

def test_mapbox_tiles_api_url():
    token = 'foo'
    map_name = 'bar'
    tile = [0, 1, 2]
    exp_url = ('https://api.mapbox.com/styles/v1/mapbox/bar/tiles'
               '/2/0/1?access_token=foo')

    mapbox_sample = cimgt.MapboxTiles(token, map_name)
    url_str = mapbox_sample._image_url(tile)
    assert url_str == exp_url

def test_mapbox_style_tiles_api_url():
    token = 'foo'
    username = 'baz'
    map_id = 'bar'
    tile = [0, 1, 2]
    exp_url = ('https://api.mapbox.com/styles/v1/'
               'baz/bar/tiles/256/2/0/1'
               '?access_token=foo')

    mapbox_sample = cimgt.MapboxStyleTiles(token, username, map_id)
    url_str = mapbox_sample._image_url(tile)
    assert url_str == exp_url

@pytest.mark.parametrize("style,extension,resolution", [
    ("alidade_smooth", "png", ""),
    ("alidade_smooth", "png", "@2x"),
    ("stamen_watercolor", "jpg", "")])
def test_stadia_maps_tiles_api_url(style, extension, resolution):
    apikey = 'foo'
    tile = [0, 1, 2]
    exp_url = ('http://tiles.stadiamaps.com/tiles/'
               f'{style}/2/0/1{resolution}.{extension}'
               '?api_key=foo')

    sample = cimgt.StadiaMapsTiles(apikey, style=style, resolution=resolution)
    url_str = sample._image_url(tile)
    assert url_str == exp_url

def test_ordnance_survey_tile_styles():
    """
    Tests that setting the Ordnance Survey tile style works as expected.

    This is essentially just assures information is properly propagated through
    the class structure.
    """
    dummy_apikey = "None"

    ref_url = "https://api.os.uk/maps/raster/v1/zxy/" \
              "{layer}/{z}/{x}/{y}.png?key=None"
    tile = ["1", "2", "3"]

    # Default is Road_3857.
    ordsurvey = cimgt.OrdnanceSurvey(dummy_apikey)
    url = ordsurvey._image_url(tile)
    assert url == ref_url.format(layer="Road_3857",
                                 z=tile[2], y=tile[1], x=tile[0])

    for layer in ("Road_3857", "Light_3857", "Outdoor_3857",
                  "Road", "Light", "Outdoor"):
        ordsurvey = cimgt.OrdnanceSurvey(dummy_apikey, layer=layer)
        url = ordsurvey._image_url(tile)
        layer = layer if layer.endswith("_3857") else layer + "_3857"
        assert url == ref_url.format(layer=layer,
                                     z=tile[2], y=tile[1], x=tile[0])

    # Exception is raised if unknown style is passed.
    with pytest.raises(ValueError):
        cimgt.OrdnanceSurvey(dummy_apikey, layer="random_style")

@pytest.mark.network
def test_ordnance_survey_get_image():
    # In order to test fetching map images from OS
    # an API key needs to be provided
    try:
        api_key = os.environ['ORDNANCE_SURVEY_API_KEY']
    except KeyError:
        pytest.skip('ORDNANCE_SURVEY_API_KEY environment variable is unset.')

    os1 = cimgt.OrdnanceSurvey(api_key, layer="Outdoor_3857")
    os2 = cimgt.OrdnanceSurvey(api_key, layer="Light_3857")

    tile = (500, 300, 10)

    img1, extent1, _ = os1.get_image(tile)
    img2, extent2, _ = os2.get_image(tile)

    # Different images for different layers
    assert img1 != img2

    # The extent is the same though
    assert extent1 == extent2

def test_azuremaps_tiles_api_url():
    subscription_key = 'foo'
    tileset_id = 'bar'
    tile = [0, 1, 2]

    exp_url = ('https://atlas.microsoft.com/map/tile?api-version=2.0'
               '&tilesetId=bar&x=0&y=1&zoom=2&subscription-key=foo')

    az_maps_sample = cimgt.AzureMapsTiles(subscription_key, tileset_id)
    url_str = az_maps_sample._image_url(tile)
    assert url_str == exp_url

@pytest.mark.network
def test_azuremaps_get_image():
    # In order to test fetching map images from Azure Maps
    # an API key needs to be provided
    try:
        api_key = os.environ['AZURE_MAPS_SUBSCRIPTION_KEY']
    except KeyError:
        pytest.skip('AZURE_MAPS_SUBSCRIPTION_KEY environment variable '
                    'is unset.')

    am1 = cimgt.AzureMapsTiles(api_key, tileset_id="microsoft.imagery")
    am2 = cimgt.AzureMapsTiles(api_key, tileset_id="microsoft.base.road")

    tile = (500, 300, 10)

    img1, extent1, _ = am1.get_image(tile)
    img2, extent2, _ = am2.get_image(tile)

    # Different images for different layers
    assert img1 != img2

    # The extent is the same though
    assert extent1 == extent2

@pytest.mark.network
@pytest.mark.parametrize('cache_dir', ["tmpdir", True, False])
def test_cache(cache_dir, tmp_path):
    if cache_dir == "tmpdir":
        tmpdir_str = str(tmp_path)
    else:
        tmpdir_str = cache_dir

    if cache_dir is True:
        config["cache_dir"] = str(tmp_path)

    # Fetch tiles and save them in the cache
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter('always')
        gt = cimgt.GoogleTiles(cache=tmpdir_str)
    gt._image_url = types.MethodType(GOOGLE_IMAGE_URL_REPLACEMENT, gt)

    ll_target_domain = sgeom.box(-10, 50, 10, 60)
    multi_poly = gt.crs.project_geometry(ll_target_domain, ccrs.PlateCarree())
    target_domain = multi_poly.geoms[0]

    img_init, _, _ = gt.image_for_domain(target_domain, 6)

    # Do not check the result if the cache is disabled
    if cache_dir is False:
        assert gt.cache_path is None
        return

    # Check that the warning is properly raised (only when cache is True)
    if cache_dir is True:
        assert len(w) == 1
    else:
        assert len(w) == 0

    # Define expected results
    x_y_z_f_h = [
        (30, 18, 6, '30_18_6.npy', '545db25f1aa348ad85e1f437fd0db0d9'),
        (30, 19, 6, '30_19_6.npy', '10355add0674bfa33f673ea27a6d1206'),
        (30, 20, 6, '30_20_6.npy', 'ab3e7f2ed8d71977ac176094973695ae'),
        (30, 21, 6, '30_21_6.npy', '3e8947b93a6ffa07f22cfea4042a4740'),
        (31, 18, 6, '31_18_6.npy', 'd0fa58b9146aa99b273eb75256b328cc'),
        (31, 19, 6, '31_19_6.npy', '9255bd0cd22736bd2c25a9087bd47b20'),
        (31, 20, 6, '31_20_6.npy', 'ac0f7e32bdf8edb50d1dccf3ec0ef446'),
        (31, 21, 6, '31_21_6.npy', 'f36b8cc1825bf267b2daead837facae9'),
        (32, 18, 6, '32_18_6.npy', '9f4ddd90cd1ae76ef2bbc8f0252ead91'),
        (32, 19, 6, '32_19_6.npy', 'a995803578bb94ecfca8563754717196'),
        (32, 20, 6, '32_20_6.npy', 'def9e71d77fd6007c77c2a14dfae858f'),
        (32, 21, 6, '32_21_6.npy', 'a3d7935037019ec58ae78f60e6fb924e'),
        (33, 18, 6, '33_18_6.npy', '4e51e32da73fb99229817dcd7b7e1f4f'),
        (33, 19, 6, '33_19_6.npy', 'b9b5057fa012c5788cbbe1e18c9bb512'),
        (33, 20, 6, '33_20_6.npy', 'b55a7c0a8d86167df496732f85bddcf9'),
        (33, 21, 6, '33_21_6.npy', '4208ba897c460e9bb0d2469552e127ff')
    ]

    # Check the results
    cache_dir_res = gt.cache_path / "GoogleTiles"
    files = list(cache_dir_res.iterdir())
    hashes = {
        f:
            hashlib.md5(
                np.load(cache_dir_res / f, allow_pickle=True).data
            ).hexdigest()
        for f in files
    }
    assert sorted(files) == [cache_dir_res / f for x, y, z, f, h in x_y_z_f_h]
    assert set(files) == gt.cache

    assert sorted(hashes.values()) == sorted(
        h for x, y, z, f, h in x_y_z_f_h
    )

    # Update images in cache (all white)
    for f in files:
        filename = cache_dir_res / f
        img = np.load(filename, allow_pickle=True)
        img.fill(255)
        np.save(filename, img, allow_pickle=True)

    gt_cache = cimgt.GoogleTiles(cache=tmpdir_str)
    gt_cache._image_url = types.MethodType(
        GOOGLE_IMAGE_URL_REPLACEMENT, gt_cache)
    img_cache, _, _ = gt_cache.image_for_domain(target_domain, 6)

    # Check that the new image_for_domain() call used cached images
    assert gt_cache.cache == gt.cache
    assert (img_cache == 255).all()


import numpy as np
import scipy.io as spio
import Plotter as plt
from scipy.stats import multivariate_normal, norm

mat = spio.loadmat('./Datasets/Dataset_2.mat', squeeze_me=True);
alpha=5*10**-3
beta=11.1

def createSQM(x,M):
    vfunc = np.vectorize(lambda x,i: x**i)
    ilist=np.arange(M)
    xlist=np.repeat(x, M)
    return vfunc(xlist,ilist)

def getXMatrix(Xlist,M):
    out = np.empty((0,M))
    for x in Xlist:
        out = np.append(out,createSQM(x,M).reshape((1,M)),axis=0)

    return out

def f_WandX(W,x):
    return np.dot(x,W)

def getMnandSninv(M,tlist,xMat):
    Sninv=alpha*np.eye(M)+beta*np.dot(xMat.T,xMat)
    MnEqns=beta*np.dot(xMat.T,tlist)
    Mn=np.linalg.solve(Sninv,MnEqns)

    return Mn, Sninv

def getE_mn(beta,Phi,Mn,t,alpha):
    return (beta*(np.linalg.norm(t-np.dot(Phi,Mn))**2)/2.0)+(alpha*np.dot(Mn.T,Mn)/2.0)

tlist=np.array(mat['y']).reshape((-1,1))
xlist=np.arange(0,1+1.0/(len(tlist)),1.0/(len(tlist)-1.0))
xFineList=np.arange(xlist[0],xlist[-1]+(xlist[-1]-xlist[0])/(1000),(xlist[-1]-xlist[0])/(1000-1))

N=len(tlist)

p_DGMlist=[]
Ap_DGMlist=[]
MnList=[]
AList=[]

snholdasdf=0

predictorList=np.array([])
sigmaxlist=np.array([])
for sudoM in range(1,11):
    Phi=getXMatrix(xlist,sudoM)
    Mn, A= getMnandSninv(sudoM,tlist,Phi)
    MnList+=[Mn]
    AList+=[A]
    E_Mn=getE_mn(beta,Phi,Mn,tlist,alpha)
    p_DGM=((sudoM*np.log(alpha)+N*np.log(beta)-np.log(np.linalg.det(A))-N*np.log(2*np.pi))/2.0)-E_Mn
    p_DGMlist+=[p_DGM[0,0]]

    Ap_DGM=(beta/(2*np.pi))**(N/2)\
        *(alpha)**(sudoM/2)\
        *(np.linalg.det(A)**(-0.5))\
        *np.exp(-E_Mn)
    Ap_DGMlist+=[Ap_DGM[0,0]]

    xFineMat=getXMatrix(xFineList,sudoM)
    f_W=f_WandX(Mn,xFineMat).reshape(-1)
    predictorList=np.append(predictorList,f_W)

    Sn=np.linalg.inv(A)
    for xp in xFineList:
        phi=createSQM(xp,sudoM).reshape((-1,1))
        sigma_2n=1/beta+np.dot(phi.T,np.dot(Sn,phi))
        sigmaxlist=np.append(sigmaxlist,sigma_2n[0,0])

predictorList=predictorList.reshape((10,-1))
sigmaxlist=sigmaxlist.reshape((10,-1))

axarr = plt.createaxis()
plt.setupaxis(axarr, "Model Complexity", "Evidance", "Plot of Evidence vs M")
plt.plot(range(0,10),Ap_DGMlist,axarr)
#plt.showOutput(FileName="Q3P6LogEvidance")
plt.showOutput()

#bestOrder=3
bestOrder=np.argmax(p_DGMlist)

for order in range(10):
    axarr = plt.createaxis()
    #plt.setupaxis(axarr, "X Values", "Y Values", "Plot of Gaussian curve fitting for M="+str(M)+" with half of the points")
    plt.setupaxis(axarr, "X Values", "Y Values", "Plot of Gaussian curve fitting for M="+str(order))
    plt.plot(xlist,tlist,axarr, graphtype="scatter")
    f_W=predictorList[order]
    plt.plot(xFineList,f_W,axarr)
    plt.plot(xFineList,f_W+np.sqrt(sigmaxlist[order]),axarr, ls='--')
    plt.plot(xFineList,f_W-np.sqrt(sigmaxlist[order]),axarr, ls='--')
    #plt.showOutput(FileName="Q3P6Best")
    #plt.showOutput(FileName="Q3P3_minus_a_few_points")
    #plt.showOutput(FileName="Q3P3")
    plt.showOutput()

###############Question 3.7
Ap_DGMlist=Ap_DGMlist/np.sum(Ap_DGMlist)
FCombined=np.dot(Ap_DGMlist,predictorList)

sigma2=0
for mui,sigmai,w in zip(predictorList,sigmaxlist,Ap_DGMlist):
    sigma2+=w*((mui-FCombined)**2+sigmai)

sigma=np.sqrt(sigma2)

axarr = plt.createaxis()
#plt.setupaxis(axarr, "X Values", "Y Values", "Plot of Gaussian curve fitting for M="+str(M)+" with half of the points")
plt.setupaxis(axarr, "X Values", "Y Values", "Plot of Gaussian curve fitting for a mixture of complexities")
plt.plot(xlist,tlist,axarr, graphtype="scatter")
plt.plot(xFineList,FCombined,axarr)
plt.plot(xFineList,f_W+sigma,axarr, ls='--')
plt.plot(xFineList,f_W-sigma,axarr, ls='--')
plt.showOutput()


import numpy as np
from scipy.io import loadmat
import pyfar as pf

def ita_ifft(data, signaltype, n_samples=None):
    """ Calculate the IFFT as done in the ITA-Toolbox for MATLAB
    The signal is assumed to be real valued in the time domain, therefore
    only the right sided spectrum is required here. Further, the frequency
    domain data is defined as its effective value inside the ITA-Toolbox
    for MATLAB, requiring a multiplication by sqrt(2) when applying the IFFT.

    Notes
    -----
        Currently only supports even numbers of samples

    Parameters
    ----------
    data : ndarray
        Data in the frequency domain
    signaltype : string
        Signal type, can either be an energy or a power signal

    Returns
    -------
    data : ndarray
        Data in the time domain

    """
    if n_samples is None:
        n_samples = 2*(data.shape[0] - 1)

    n_samples_is_even = ((n_samples % 2) == 0)

    if signaltype == 'power':
        data *= n_samples
        if n_samples_is_even:
            data[-1, :] = data[-1, :] * np.sqrt(2)
            data[1:-2, :] = data[1:-2, :] / np.sqrt(2)
        else:
            data[1:-1, :] = data[1:-1, :] / np.sqrt(2)
    return np.fft.irfft(data, axis=0)

def read_ita(filename):
    """Read .ita audio files. The standard audio file format of the
    ITA-Toolbox for MATLAB. This function returns the time data of the signal
    regardless of the domain the signal was stored in inside the file.

    Notes
    -----
        This function currently only works with .ita files saved in the MATLAB
        file format with versions v4, v6, and v7 to v7.2. Version v7.3 MATLAB
        files are not supported yet.

    Parameters
    ----------
    filename : string
        Name of the file to be read

    Returns
    -------
    data : ndarray, double
        Time data stored in the file
    samplingrate : integer
        Sampling rate of the signal
    type : string
        Signal type, can either be an energy or a power signal

    """
    matfile = loadmat(
        filename, struct_as_record=False, squeeze_me=True, appendmat=False)
    mfiledata = matfile['ITA_TOOLBOX_AUDIO_OBJECT']

    if mfiledata.domain == 'time':
        data = mfiledata.data
        try:
            samplingrate = mfiledata.samplingRate
        except AttributeError:
            times = mfiledata.abscissa
            return pf.TimeData(data.T, np.squeeze(times))

    elif mfiledata.domain == 'freq':
        data = ita_ifft(mfiledata.data, mfiledata.signalType)
        try:
            samplingrate = mfiledata.samplingRate
        except AttributeError:
            freqs = mfiledata.abscissa
            return pf.FrequencyData(data.T, np.squeeze(freqs))

    if mfiledata.signalType == 'energy':
        norm = 'none'
    else:
        norm = 'rms'

    return pf.Signal(data.T, samplingrate, fft_norm=norm)


from collections import deque

class Solution:
    def orangesRotting(self, grid) -> int:
        queue = deque()
        time,fresh = 0,0

        for x in range(len(grid)):
            for y in range(len(grid[0])):
                if grid[x][y]==1:
                    fresh+=1
                if grid[x][y]==2:
                    queue.append([x,y])
        directions = [[1,0],[-1,0],[0,1],[0,-1]]
        while queue and fresh >0:
            for ab in range(len(queue)):
                r,c = queue.popleft()
                for cd in directions:
                    row,col = r+cd[0], c+cd[1]
                    if row>=0 and col>=0 and row<len(grid) and col<len(grid[0]) and grid[row][col]==1:
                        queue.append([row,col])
                        grid[row][col]=2
                        fresh-=1
                    else:
                        continue
            time+=1
        return time if fresh==0 else -1


import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--headless")
options.add_argument("window-size=1400,1500")
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
options.add_argument("start-maximized")
options.add_argument("enable-automation")
options.add_argument("--disable-infobars")
options.add_argument("--disable-dev-shm-usage")

driver = webdriver.Chrome(options=options)

# s = Service("/Users/soprano/Desktop/First_Module/pythonSelenium/chromedriver ")
# driver = webdriver.Chrome(service=s)

driver.get("http://webdriveruniversity.com/index.html")

def test_Ajax_Loader():
    driver.find_element(By.CSS_SELECTOR, "#ajax-loader").click()
    ajax_loader_window = driver.window_handles[1]
    driver.switch_to.window(ajax_loader_window)

    wait = WebDriverWait(driver, 10)
    wait.until(EC.element_to_be_clickable((By.XPATH, "//span[@type='button']")))
    driver.find_element(By.XPATH, "//span[@type='button']").click()

    wait.until(EC.visibility_of_element_located((By.TAG_NAME, "h4")))
    assert "Well Done For Waiting....!!!" in driver.find_element(By.TAG_NAME, "h4").text

    time.sleep(3)
    driver.close()
    driver.switch_to.window(driver.window_handles[0])
    HomePage_header = driver.find_element(By.CSS_SELECTOR, "#udemy-promo-thumbnail h1").text
    assert "My Courses & Promo Codes" in HomePage_header, "Maybe, You're not at Home Page"
    driver.close()

test_Ajax_Loader()


import setuptools

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

setuptools.setup(
    name="my_utils",
    version="0.0.3",
    author="Krzysztof Dobrowolski",
    author_email="k.dobrowolski1990@gmail.com",
    description="Package to create supporting functions",
    long_description=long_description,
    long_description_content_type="text/markdown",
    packages=setuptools.find_packages(where='src', exclude=('*test*', )),
    package_dir={"": "src"},
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires='>=3.8',
    install_requires = ['pandas', 'requests'],
    extras_require = {
        'dev': [
            'unitest'
        ],
    }
)


from typing import Optional

import discord
from discord.ext import commands
from discord.ext.commands import Context

from src.parametros import Types
from src.db.queries import Queries

class CardCommand(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.query: Queries = Queries()

    @commands.command(name="card", aliases=["c"])
    async def card_command(self, ctx: Context):
        message: str = ctx.message.content
        message_l: list[str] = message.split(" ")
        card_name: str = " ".join(message_l[1:]).strip()
        card: Optional[dict] = self.query.get_card(card_name, type_=Types.CARD)
        if card is None:
            embed_e: discord.Embed = discord.Embed(title="There has been an error", description="The card you are looking for doesn't exist D: srry", colour=discord.Color.dark_red())
            await ctx.send(embed=embed_e)

        else:
            name: str = card.get("name")
            function: str = card.get("function")
            id_: int = card.get("id")
            type_f: Types = card.get("type")
            unlock: str = card.get("unlock")
            pickup: str = card.get("pickup")
            link: dict = self.query.get_link_by_id(id_, type_=Types.CARD)
            embed_a: discord.Embed = discord.Embed(title=name, description=f"**Description:  **"+function, colour=discord.Color.teal())
            embed_a.add_field(name="*Type:* ", value=type_f, inline=True)
            embed_a.add_field(name="*ID:* ", value=id_, inline=True)
            embed_a.add_field(name="*Pickup:* ", value=pickup, inline=True)
            if unlock is not None:
                embed_a.add_field(name="*Unlock method:* ", value=unlock, inline=False)
            if link:
                link_f: str = link.get("url")
                embed_a.set_thumbnail(url=link_f)
            await ctx.send(embed=embed_a)

async def setup(bot):
    await bot.add_cog(CardCommand(bot))


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

# # DATA ACQUISITION (WEB SCRAPPING) :

# In[1]:

from urllib.request import urlopen
# Copying the URL of web from which u want to scrap data
url="https://en.wikipedia.org/wiki/Android_version_history"
data=urlopen(url) # Aquiring the data by opening the URL
url_html=data.read() # Read URL in the form of HTML
print(url_html)
data.close #closing the data

# ## BEAUTIFUL SOUP:

# In[2]:

# Beautiful soup are use for extracting specific data from the set of HTML
from bs4 import BeautifulSoup as soup
# BeautifulSoup is a class
android_soup=soup(url_html,'html.parser')
print(android_soup)

# In[3]:

# Finding the main heading of a web page
main_heading=android_soup.find_all('h1',{}) # h1 represent heading in HTML and {} use for filteration
print(main_heading)

# In[5]:

for i in main_heading:
    print(i.text)  # i.text used to convert HTML codes into text form

# In[6]:

# Extracting all tables
all_tables=android_soup.find_all('table',{'class':'wikitable'})
print(all_tables)

# In[9]:

for i in all_tables:
    print(i.text)

# In[15]:

#print the heading of a table 0
table_headings=all_tables[0].find_all('th',{})
for i in table_headings:
    print(i.text)

# Store in the form of list
table_headings=[i.text[:-1] for i in table_headings]
print(table_headings)

# In[18]:

#print all the rows of the table 0
all_rows=all_tables[0].find_all('tr',{})
for i in all_rows:
    print(i.text)

# In[85]:

# store all the rows in the form of list
table_rows=[]
for row in all_rows:
    current_row=[]
    x=row.find_all('td',{})
    for idx,data in enumerate(x):
            if idx==2:
                current_row.append(data.text[:-1].replace(',',''))
            else:
                current_row.append(data.text[:-1]) # -1 is used to remove last element '\n'
    table_rows.append(current_row)
print(table_rows)

# ### CSV FILE :

# In[86]:

file_name="android_version.csv"
with open(file_name,'w',encoding='utf-8') as f:
    heading_string=', '.join(table_headings) # join the list
    heading_string+='\n'
    f.write(heading_string)
    for row in table_rows:
        row_string=""
        row_string+=', '.join(row)
        row_string+='\n'
        f.write(row_string)

# ## Pandas :

# In[87]:

# Pandas are use to convert CSV files into tabular informations
import pandas as pd
panda_data=pd.read_csv("android_version.csv")
panda_data.head(10) #print first 10 rows

# In[88]:

panda_data.iloc[5][0]

DATA AQUISITION/Data Aquasition - Web Scrapping.py


import random, time

from segmentation import Segmentation, SegmentationBatch
from utils import cvtSecond2HMS

seed = 9001
random.seed(seed)

def run_unet(istrain=False, tissue='liver', batch_size=8, image_size=(512, 512), model_pretrain=None):
    return Segmentation.run_liver(istrain=istrain,
                                  model_name='unet',
                                  modelcheckpoint='cache/{0}/model/unet_{1}.hdf5'.format(tissue, image_size[0]),
                                  batch_size=batch_size,
                                  nb_epoch=50,
                                  model_pretrain=model_pretrain,
                                  isliver=True if tissue=='liver' else False,
                                  image_size=image_size)

def run_UnetSimpleResNet(istrain=False, tissue='liver', batch_size=4, image_size=(512, 512), model_pretrain=None, is_datagen=False):
    return Segmentation.run_liver(istrain=istrain,
                                  model_name='UnetSimpleResNet',
                                  modelcheckpoint='cache/{0}/model/UnetSimpleResNet_{1}.hdf5'.format(tissue, image_size[0]),
                                  batch_size=batch_size,
                                  nb_epoch=50,
                                  model_pretrain=model_pretrain,
                                  isliver=True if tissue == 'liver' else False,
                                  image_size=image_size,
                                  is_datagen=is_datagen)

def run_unet_25D(istrain=False):
    return SegmentationBatch.run_liver(istrain=istrain,
                                       model_name='unet',
                                       modelcheckpoint='cache/liver/model/unet_25D.hdf5',
                                       batch_size=8,
                                       nb_epoch=20,
                                       channel=5)

def run_unet_standard_25D(istrain=False):
    return SegmentationBatch.run_liver(istrain=istrain,
                                       model_name='unet_standard',
                                       modelcheckpoint='cache/liver/model/unet_standard_25D.hdf5',
                                       batch_size=4,
                                       nb_epoch=20,
                                       channel=5)

def run_unet_resnet(istrain=False, tissue='liver', batch_size=8):
    return Segmentation.run_liver(istrain=istrain,
                                  model_name='unet_resnet',
                                  modelcheckpoint='cache/{0}/model/unet_resnet.hdf5'.format(tissue),
                                  batch_size=batch_size,
                                  nb_epoch=50,
                                  isliver=True if tissue=='liver' else False)

if __name__ == '__main__':
    # print_env()

    ts = time.clock()

    # (X_test, y_test, predicts) = run_unet(istrain=True)       # dice = 93
    (X_test, y_test, predicts) = run_UnetSimpleResNet(istrain=True)     # dice =
    # (X_test, y_test, predicts) = run_unet_25D(istrain=True)  # dice = 90.5
    # (X_test, y_test, predicts) = run_unet_standard_25D(istrain=False)  # dice = 88.7

    # (X_test, y_test, predicts) = run_unet(istrain=True, tissue='tumor', batch_size=32)  # dice =
    (X_test, y_test, predicts) = run_UnetSimpleResNet(istrain=True, tissue='tumor', batch_size=16, image_size=(256, 256),
                                                      model_pretrain='cache/liver/model/UnetSimpleResNet_512.hdf5', is_datagen=True)  # dice =

    print("total process time: ", cvtSecond2HMS(time.clock() - ts))
    #
    # for i in range(0, 1):
    #     if keras.backend.image_data_format() == 'channels_last':
    #         showImages(X_test[i, :, :, 0], y_test[i, :, :, 0], predicts[i, :, :, 0])
    #     if keras.backend.image_data_format() == 'channels_first':
    #         showImages(X_test[i, 0, :, :], y_test[i, 0, :, :], predicts[i, 0, :, :])
    # # for (image, mask, predict) in zip(X_test, y_test, predicts):
    # #     seg.show(image[0, :, :], mask[0, :, :], predict[0, :, :])
    #
    # plt.show()


import datetime
import json
import logging
import os
import random

from telegram import ParseMode
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters

from settings import BOT_KEY, PROXY

logging.basicConfig(
    format='%(levelname)s %(asctime)s: %(message)s',
    level=logging.INFO,
    filename='bot.log'
)

DEV_CHAT_ID = 309870404
SMILING_IMP = '😈'

JOKES = [
    'Дела идут, контора пишет, а касса деньги выдает',
    'Гуляй, Вася, жуй опилки, я - директор лесопилки',
    '¯\_(ツ)_/¯',
    'дили-дили, трали-вали',
    '',  # sometimes we should be serious
]

EXCLUDE_DATES = {
    '2019-05-01',
    '2019-05-02',
    '2019-05-03',
    '2019-05-04',
    '2019-05-05',

    '2019-05-09',
    '2019-05-10',
    '2019-05-11',
    '2019-05-12',

    '2019-06-12',

    '2019-11-04',

    '2019-12-31',

    '2020-01-01',
    '2020-01-02',
    '2020-01-03',
    '2020-01-04',
    '2020-01-05',
    '2020-01-06',
    '2020-01-07',
    '2020-01-08',

    '2020-02-24',

    '2020-03-09',

    '2020-05-01',
    '2020-05-04',
    '2020-05-05',
    '2020-05-11',

    '2020-06-12',

    '2020-11-04',
}

def log_user(user_obj):
    logging.info('User %s: %s %s', user_obj.id, user_obj.first_name, user_obj.last_name)

def all_clear(update, context):
    update.message.reply_text('Понятно')
    log_user(update.message.from_user)

def shrug(update, context):
    update.message.reply_text('¯\_(ツ)_/¯')

# def mention_all(update, context):
#     print('Вызван /all')
#     print(update.message.from_user.id, update.message.from_user.first_name, update.message.from_user.last_name)
#     log_user(update.message.from_user)
#     update.message.reply_html(MENTION_ALL)

# def mention_it(update, context):
#     log_user(update.message.from_user)
#     update.message.reply_html(MENTION_IT)

# def mention_managers(update, context):
#     log_user(update.message.from_user)
#     update.message.reply_html(MENTION_MANAGERS)

def show_help(update, context):
    log_user(update.message.from_user)
    update.message.reply_text(
        'Доступные команды:\n'
        '/all <text> - отправить уведомление всем\n'
        '/it <text> - отправить уведомление всем айтишникам\n'
        '/mng <text> - отправить уведомление всем менеджерам\n'
        '/shrug - deprecated. use builtin :shrug:\n'
        '/yoj_tebe_ponyatno\n'
        '/tasks - вывод задач Макса\n'
        '/add <text>\n'
        '/push <text> - добавить задачу Максу в конец списка\n'
        '/del <num>\n'
        '/pop <num> - убрать задачу номер num из списка\n'
        '/up <num> - поднять задачу номер num\n'
        '/down <num> - опустить задачу номер num\n'
        '/highest <num> - назначить задаче номер num наивысший приоритет\n'
        '/lowest <num> - назначить задаче номер num низший приоритет\n'
        '/help - этот вывод'
    )

# def timesheet_reminder(context):
#     job = context.bot
#     if datetime.datetime.now().weekday() in (5,6):
#         return
#     if datetime.datetime.now().strftime('%Y-%m-%d') in EXCLUDE_DATES:
#         return
#     context.bot.send_message(
#         chat_id=IQOS_GROUP_CHAT_ID,
#         text='Дамы и господа! Таймшит, будь он неладен!\n{}'.format(MENTION_ALL),
#         parse_mode=ParseMode.HTML
#     )

# TASK LIST
def check_permissions(update):
    allowed_users = ["generous_wind", ]
    # allowed_users = ["rumyantsevr"]
    if update.message.from_user.username not in allowed_users:
        update.message.reply_text('Sorry. No.')
        return False
    return True

def get_task_list():
    with open('task_list.json') as f:
        tasks = json.load(f)
        return tasks

def set_task_list(tasks):
    with open('task_list.json', 'w') as f:
        json.dump(tasks, f)

def task_list(update, context):
    logging.info("Умом Россию не понять...")
    user_obj = update.message.from_user
    print('TASKS INVOKED: User %s: %s %s' % (user_obj.id, user_obj.first_name, user_obj.last_name))
    tasks = get_task_list()
    if tasks:
        msg = '\n'.join(
            '{} {}'.format(c, task)
            for c, task in enumerate(tasks)
        )
    else:
        msg = random.choice(JOKES)
    # update.message.reply_markup('{}'.format(msg))
    if not msg:
        msg = 'The work is done \\(^.^)/'
    context.bot.send_message(
        chat_id=update.message.chat.id,
        # text='```\n{}\n```'.format(msg),
        text=msg,
        # parse_mode=ParseMode.MARKDOWN
    )

def push(update, context):
    has_perm = check_permissions(update)
    if not has_perm:
        return
    if not context.args:
        update.message.reply_text('You should add task info')
    tasks = get_task_list()
    tasks.append(' '.join(context.args))
    set_task_list(tasks)
    # update.message.reply_text('`Added {}`'.format(' '.join(context.args)))
    context.bot.send_message(
        chat_id=update.message.chat.id,
        text='`Added: {} {}`'.format(len(tasks)-1, ' '.join(context.args)),
        parse_mode=ParseMode.MARKDOWN
    )

def pop(update, context):
    has_perm = check_permissions(update)
    if not has_perm:
        return
    tasks = get_task_list()
    if not context.args or not context.args[0].isdigit() or int(context.args[0]) > len(tasks):
        update.message.reply_text('Wrong argument')
        return
    task_num = int(context.args[0])
    removed = tasks.pop(task_num)
    # update.message.reply_text('Marked **{}** as done.'.format(removed))
    context.bot.send_message(
        chat_id=update.message.chat.id,
        text='Marked `{}` as done.'.format(removed),
        parse_mode=ParseMode.MARKDOWN
    )
    set_task_list(tasks)

def task_up(update, context):
    args = context.args
    has_perm = check_permissions(update)
    if not has_perm:
        return
    tasks = get_task_list()
    if not args or not args[0].isdigit() or int(args[0]) > len(tasks) and int(args[0]) == 0:
        update.message.reply_text('Wrong argument')
        return
    task_num = int(args[0])
    moved = tasks.pop(task_num)
    tasks.insert(task_num-1, moved)
    context.bot.send_message(
        chat_id=update.message.chat.id,
        text='Moved `{}` 1 position upper.'.format(moved),
        parse_mode=ParseMode.MARKDOWN
    )
    set_task_list(tasks)

def task_down(update, context):
    args = context.args
    has_perm = check_permissions(update)
    if not has_perm:
        return
    tasks = get_task_list()
    if not args or not args[0].isdigit() or int(args[0]) > len(tasks):
        update.message.reply_text('Wrong argument')
        return
    task_num = int(args[0])
    moved = tasks.pop(task_num)
    tasks.insert(task_num+1, moved)
    context.bot.send_message(
        chat_id=update.message.chat.id,
        text='Moved `{}` 1 position lower.'.format(moved),
        parse_mode=ParseMode.MARKDOWN
    )
    set_task_list(tasks)

def highest(update, context):
    args = context.args
    has_perm = check_permissions(update)
    if not has_perm:
        return
    tasks = get_task_list()
    if not args or not args[0].isdigit() or int(args[0]) > len(tasks):
        update.message.reply_text('Wrong argument')
        return
    task_num = int(args[0])
    moved = tasks.pop(task_num)
    tasks.insert(0, moved)
    context.bot.send_message(
        chat_id=update.message.chat.id,
        text='Moved `{}` to top.'.format(moved),
        parse_mode=ParseMode.MARKDOWN
    )
    set_task_list(tasks)

def lowest(update, context):
    args = context.args
    has_perm = check_permissions(update)
    if not has_perm:
        return
    tasks = get_task_list()
    if not args or not args[0].isdigit() or int(args[0]) > len(tasks):
        update.message.reply_text('Wrong argument')
        return
    task_num = int(args[0])
    moved = tasks.pop(task_num)
    tasks.append(moved)
    context.bot.send_message(
        chat_id=update.message.chat.id,
        text='Moved `{}` to bottom.'.format(moved),
        parse_mode=ParseMode.MARKDOWN
    )
    set_task_list(tasks)

# def greet_newcomers(update, context):
#     if update.message.new_chat_members:
#         log_user(update.message.from_user)
#         usernames = list()
#         user_ids = list()
#         for new_user in update.message.new_chat_members:
#             logging.info(
#                 'User joined id: %s, first_name: %s, last_name: %s, username: %s,',
#                 new_user.id, new_user.first_name, new_user.last_name, new_user.username
#             )
#             if new_user.username:
#                 usernames.append(new_user.username)
#             else:
#                 user_ids.append((new_user.id, new_user.first_name))
#         mention_string = list()
#         mention_string.append(' '.join(['@{}'.format(u) for u in usernames]))
#         for first_name, user_id in user_ids:
#             mention_string.append('{}'.format(user_id, first_name))

#         update.message.reply_html(
#             '{}\nWELCOME TO HELL {}'.format(' '.join(mention_string), SMILING_IMP)
#         )

# def listen_to_channel(update, context):
#     if update.message.text:
#         print(update.message.text)
#         log_user(update.message.from_user)

def test3(update, context):
    link = update.message.text.split()[-1]
    update.message.reply_html('{}'.format(link, link))

def main():
    print('RUN')

    if BOT_KEY == '':
        raise ValueError('BOT_KEY is empty. It should be defined in local_settings.py')

    # create task_list.json if needed
    if not os.path.exists('task_list.json'):
        with open('task_list.json', 'w') as f:
            f.write('[]')

    mybot = Updater(BOT_KEY, use_context=True, request_kwargs=PROXY)

    job_queue = mybot.job_queue
    # job_queue.run_repeating(
    #     timesheet_reminder, interval=24*60*60, first=datetime.time(hour=17, minute=30),
    # )

    dp = mybot.dispatcher
    dp.add_handler(CommandHandler("test3", test3))

    dp.add_handler(CommandHandler("help", show_help))
    dp.add_handler(CommandHandler("shrug", shrug))
    dp.add_handler(CommandHandler("yoj_tebe_ponyatno", all_clear))
    # dp.add_handler(CommandHandler("all", mention_all))
    # dp.add_handler(CommandHandler("it", mention_it))
    # dp.add_handler(CommandHandler("mng", mention_managers))

    dp.add_handler(CommandHandler("tasks", task_list))
    dp.add_handler(CommandHandler("push", push))
    dp.add_handler(CommandHandler("add", push))
    dp.add_handler(CommandHandler("pop", pop))
    dp.add_handler(CommandHandler("del", pop))
    dp.add_handler(CommandHandler("up", task_up))
    dp.add_handler(CommandHandler("down", task_down))
    dp.add_handler(CommandHandler("highest", highest))
    dp.add_handler(CommandHandler("lowest", lowest))

    # dp.add_handler(MessageHandler(
    #     Filters.status_update.new_chat_members, greet_newcomers
    # ))
    # dp.add_handler(MessageHandler(Filters.text, listen_to_channel))

    mybot.start_polling()
    mybot.idle()

if __name__ == '__main__':
    main()


from collections import OrderedDict

# Dictionaries sorting Tokens by hierarchy. Hierarchies are based on game importance and similarity.

FLOOR_TOKENS = OrderedDict(
    {
        "-": "grass",
     }
)

WALL_TOKENS = OrderedDict(
    {
        "W": "wall",
    }
)

ROAD_TOKENS = OrderedDict(
    {
        "R": "road",
     }
)

SPECIAL_TOKENS = OrderedDict(
    {
        "S": "starting line",
        "Q": "question block",
        "O": "oil",
        "C": "coin",
        "<": "boost",
     }
)

TOKEN_DOWNSAMPLING_HIERARCHY = [
    FLOOR_TOKENS,
    WALL_TOKENS,
    ROAD_TOKENS,
    SPECIAL_TOKENS,
]

TOKENS = OrderedDict(
    {**FLOOR_TOKENS, **WALL_TOKENS, **ROAD_TOKENS, **SPECIAL_TOKENS}
)

TOKEN_GROUPS = [FLOOR_TOKENS, WALL_TOKENS, ROAD_TOKENS, SPECIAL_TOKENS]

REPLACE_TOKENS = {}


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Dec 30 22:22:28 2020

@author: Kimi Kreilgaard & Jonathan Steensgaard & Amalie Beate

"""

import numpy as np
import matplotlib.pyplot as plt

import seaborn as sns
from iminuit import Minuit
import sys
from scipy import stats, optimize
from scipy.optimize import minimize

from sympy import *
from sympy import sympify

from ExternalFunctions import *

from IPython.display import display
from IPython.core.display import Latex
def lprint(*args,**kwargs):
    """Pretty print arguments as LaTeX using IPython display system

    Parameters
    ----------
    args : tuple
        What to print (in LaTeX math mode)
    kwargs : dict
        optional keywords to pass to `display`
    """
    display(Latex('$$'+' '.join(args)+'$$'),**kwargs)

def weighted_avg(val, err, plot=False, title=None):
    """
    INPUT:
    val = values, array_like
    err = erros, array_like
    plot = option to plot or not

    """

    # Calculate the avg according to Barlow (4.6)
    avg = np.sum( (val / err**2) / np.sum( 1 / err**2 ) )

    # Calculate the error
    avg_sig = np.sqrt( 1 / np.sum(1 / err**2) )

    # Find degrees of freedom (-1 )
    N_dof = len(val) - 1

    # Calculate chi_square
    chi2 = np.sum( (val - avg)**2 / err**2 )

    # Calculate p-value (the integral of the chi2-distribution from chi2 to infinity)
    p = stats.chi2.sf(chi2, N_dof)

    # Option to plot the fitted line
    if plot:

        # Create figure
        fig, ax = plt.subplots(figsize=(12,6))

        # X values are measurement number
        x = np.arange(len(val))+1

        # Plot values and errorbars
        ax.scatter(x, val)
        ax.errorbar(x, val, err, fmt='ro', ecolor='k', elinewidth=1, capsize=2, capthick=1)

        #Plot the weighted average line
        ax.hlines(avg, 0, len(val)+0.5, colors = 'r', linestyle = 'dashed')

        # Nice text
        d = {'mu':   avg,
             'sigma_mu': avg_sig,
             'Chi2':     chi2,
             'ndf':      N_dof,
             'Prob':     p,
            }

        text = nice_string_output(d, extra_spacing=2, decimals=3)
        add_text_to_ax(0.02, 0.95, text, ax, fontsize=14)
        ax.set_title(title, fontsize=18)
        fig.tight_layout()

    return avg, avg_sig, chi2, p

def value_error_contribution_func_gen(expr, variables):
    """
    expr = takes in a math expression in a string of type 'a+b'
    var = takes in a tuple of variables strings, fx ('a', 'b')
    """
    # Convert expression into a sympy expression
    expr = parse_expr(expr)

    # Define sympy symbols for the parameters (the tuple variables) and the standard deviations
    var_symbols = symbols(variables)
    err_symbols = symbols( tuple("sigma_" + k for k in variables) )

    # Find expressions for each contributions
    contributions = [expr.diff(var) ** 2 * err**2 for var, err in zip(var_symbols, err_symbols)]

    # Convert contributions to numerical functions
    f_contributions = [ lambdify(var_symbols + err_symbols, expression) for expression in contributions ]

    # Find the error propagation expression to be evaluated, and display
    expr_sig = sqrt( sum(contributions) )
    display(expr_sig)

    # Convert the expression for the value and the error into numerical functions
    f_val = lambdify(var_symbols, expr)
    f_err = lambdify(var_symbols + err_symbols, expr_sig)

    def func(**kwargs):
        """
        Define a function that will take in keywordarguments **kwargs which is a dictionary of type:
        {'a':(1,0.1), 'b':(2,0.3)}. Kwargs.values calls the two tuples as one list [(1,0.1),(2,0.3)].
        From there an array of variables and an array of errors can be extracted and the numerical
        functions found above can be used.

        """
        # Create tuple of values of variables
        v = tuple(v[0] for v in kwargs.values())

        # Create tuple of errors of variables
        s = tuple(v[1] for v in kwargs.values())

        # Calculate value and error
        value, error = f_val(*v), f_err(*v, *s)

        # Calculate contribution from each variable
        contr_list = [ function(*v,*s) for function in f_contributions ]

        #Return value and analytical error
        return value, error, contr_list

    # Return the main function that we set out to generate
    return func

# Define function that gets variables from **kwargs and uses the function above to return value and error
def val_err_contr(expr, **kwargs):
    """
    INPUT:
    expr = takes in a math expression in a string of type 'a+b'
    **kwargs = variable names = (value, error) of type a=(3, 0.3)

    Note that if the relation depends on constant, type those in as variables with sigma = zero.

    OUTPUT:
    value = integer
    error = integer
    contributions = array_like with contributions from each variable in the same order as in the input
    """

    return value_error_contribution_func_gen(expr, tuple(kwargs))(**kwargs)

def binom_prob(r, n , p, plot=True):
    """
    INPUT: r = number of succeses, n = number of trials, p = probability of succes.

    OUTPUT: Probability of r
    """

    # Calculate probability of r succeses, and print
    prob_r = stats.binom.pmf(r, n, p)
    print(f'The probability of {r:.0f} succeses is {prob_r:.4f}')

    # Option to plot
    if plot:

        # Create figure
        fig, ax = plt.subplots(figsize=(10, 6))

        # Make an array of r's to evaluate the probability of (centered around the mean)
        mean = n*p
        variance = n*p*(1-p)

        # Plot
        xaxis = np.linspace( mean - variance, mean + variance, 1000)
        yaxis = stats.binom.pmf(np.round(xaxis), n, p)
        ax.plot(xaxis, yaxis, '-', label = 'Binomial pmf', zorder=1, color='k')

        # Nice text
        d = {'Number of trials': n,
             'Prob of succes': p,
             'Mean': mean,
             'Std': np.sqrt(variance),}

        text = nice_string_output(d, extra_spacing=2, decimals=3)
        add_text_to_ax(0.02, 0.87, text, ax, fontsize=12)
        ax.set(title='Binomial Probability', xlabel='Number of succeses r', ylabel='Probability')

        ax.scatter(r, stats.binom.pmf(r,n,p), color = 'r', label=f'Probability of {r} successes', zorder=2)
        ax.legend(loc='upper left')

def binom_trials(r, p, prob_r, test_range, plot=True):
    """
    INPUT:
    r = number of succeses we want guaranteed,
    p = probability of succes in each trial
    prob_r = statistically guaranteed probability of getting at least r number of succeses in n trials
    test_range = (low, high), limits of the arrays of n's we will test

    OUTPUT:
    n = number of trials to ensure the prob_r
    """

    # Create an array of n trials to test
    test_array = np.arange(*test_range, 1)

    # We will reduce the array to something bigger than len(3) through binary search
    while len(test_array) > 3:

        # Choose the median of the array
        middle_index = np.int( np.floor( len(test_array)/2 ) ) + 1
        n_median = test_array[middle_index]

        # Test the survival function of this n. Notice we use r-1 since we want all successes > r-1
        sf = stats.binom.sf(r-1, n_median, p)

        # If the value is under 90% we look in the second half of the array since we need more trials
        # Else we look in the lower half of the array
        if sf < prob_r:
            test_array = test_array[middle_index:]

        if sf > prob_r:
            test_array = test_array[:middle_index]

    # We will now start from the first element (the lowest n) in the test array and calculate the sf
    # the moment it surpasses prob_r we return that n
    n = test_array[0]
    sf = stats.binom.sf(r-1, n, p)

    while sf < prob_r:
        n += 1
        sf = stats.binom.sf(r-1, n ,p)

    # If we obtain the guaranteed succesrate, return the given n
    if sf >= prob_r:

        # Print result
        print(f'To be {prob_r*100:.0f}% sure of {r:.0f} success(es), we need {n} trials')

        # The option to plot
        if plot:

            fig, ax = plt.subplots(ncols=2, figsize=(15,6), sharex=True,
                                   gridspec_kw = {'width_ratios': [1.2, 1.5], 'wspace': 0.2})
            fig.suptitle(f'Binomial Distribution with n={n} and p={0.054}', fontsize=18)

            ### PLOT BINOMIAL PMF ###

            # Make an array of r's to evaluate the probability of (centered around the mean)
            mean = n*p
            variance = n*p*(1-p)

            xaxis = np.linspace( mean - variance, mean + variance, 1000)
            yaxis = stats.binom.pmf(np.round(xaxis), n, p)

            ax[0].plot(xaxis, yaxis, '-', label = 'Binomial pmf', zorder=1, color='k')
            ax[0].scatter(r-1, stats.binom.pmf(r-1,n,p), color='r', zorder=2)
            ax[0].set(title='Probability Mass Function',
                      xlabel='Number of succeses r', ylabel='Probability')

            # Define the vertical line that seperates the bins we dont want to end with the ones we want
            ax[0].fill_between(x = [0, r-0.52], y1 = 0, y2 = 1, color='red', alpha=0.1)
            ax[0].fill_between(x = [r-0.5, xaxis[-1]], y1 = 0, y2 = 1, color='green', alpha=0.1)

            ### PLOT SURVIVAL FUNCTION ###
            ax[1].plot(xaxis, stats.binom.sf(np.round(xaxis), n, p), zorder=1, color='k')
            ax[1].scatter(r-1, stats.binom.sf(r-1,n,p),color='r',zorder=2)
            ax[1].set(title='Survival Function', xlabel='Number of succeses r',
                      ylabel='Probability of r or more successes')

            # Define horisontal line that seperates the bins of confidence we are interested in
            ax[1].fill_between(x = [0, xaxis[-1]], y1 = 0.9, y2 = 1, color='red', alpha=0.1)
            ax[1].fill_between(x = [0, xaxis[-1]], y1 = 0, y2 = 0.898, color='green', alpha=0.1)

        return n

def poisson_trials(r, p, prob_r, guess = 1, plot=True):
    """
    INPUT:
    r = number of succeses we want guaranteed,
    p = probability of succes in each trial
    prob_r = statistically guaranteed probability of getting at least r number of succeses in n trials
    test_range = (low, high), limits of the arrays of n's we will test

    OUTPUT:
    n = number of trials to ensure the prob_r
    """

    # Solve for lambda that gives at least prob_r success
    #Notice we use r-1 since we want all successes > r-1
    root = optimize.root(lambda lamb: stats.poisson.sf(r-1, lamb) - prob_r, guess)

    # If succesful, continue
    if root.success:

        # Extract solution
        lamb = root.x[0]

        # Calculate number of trials, and print. We ceal since we need the next integer.
        n = np.int ( np.ceil(lamb / p) )
        print(f'To be {prob_r*100:.0f}% sure of {r:.0f} success(es), we need {n} trials')

        # Option to plot
        if plot:

            fig, ax = plt.subplots(ncols=2, figsize=(15,6), sharex=True,
                                   gridspec_kw = {'width_ratios': [1.2, 1.5], 'wspace': 0.2})
            fig.suptitle(f'Poisson Distribution with lambda={lamb:.1f}, p={0.054} and thus n={n}', fontsize=18)

            ### PLOT POISSON PMF ###

            # Make an array of r's to evaluate the probability of (centered around the mean)
            mean, variance = lamb, lamb

            xaxis = np.linspace( mean - variance, mean + variance, 1000)
            yaxis = stats.poisson.pmf(np.round(xaxis), lamb)

            ax[0].plot(xaxis, yaxis, '-', label = 'Poisson pmf', zorder=1, color='k')
            ax[0].scatter(r-1, stats.poisson.pmf(r-1,lamb),color='r', zorder=2)
            ax[0].set(title='Probability Mass Function',
                      xlabel='Number of succeses r', ylabel='Probability')

            # Define the vertical line that seperates the bins we dont want to end with the ones we want
            ax[0].fill_between(x = [0, r-0.52], y1 = 0, y2 = 1, color='red', alpha=0.1)
            ax[0].fill_between(x = [r-0.5, xaxis[-1]], y1 = 0, y2 = 1, color='green', alpha=0.1)

            ### PLOT SURVIVAL FUNCTION ###
            ax[1].plot(xaxis, stats.poisson.sf(np.round(xaxis), lamb), zorder=1, color='k' )
            ax[1].scatter(r-1, stats.poisson.sf(r-1, lamb),color='r',zorder=2)
            ax[1].set(title='Survival Function', xlabel='Number of succeses r',
                      ylabel='Probability of r or more successes')

            # Define horisontal line that seperates the bins of confidence we are interested in
            ax[1].fill_between(x = [0, xaxis[-1]], y1 = 0.9, y2 = 1, color='red', alpha=0.1)
            ax[1].fill_between(x = [0, xaxis[-1]], y1 = 0, y2 = 0.898, color='green', alpha=0.1)

        return n

    # If unsuccesful we probably need another guess
    else: print('A solution could not be obtained. Try another guess.')

def cum(r, p, limit, trials, binom=False, poisson=False, plots=False):

    """
    r = number of events
    p = probabillity of each event
    limit = The lower limit of succes of r
    trials = bound with the test range

    Choose binomial or poisson distribution
    """
    # We set up a test array:
    n_trials = np.arange(*trials, 1)

    # we check the first index:
    n = n_trials[0]

    if binom :
        y = stats.binom.sf(r - 1, n, p)
    if poisson:
        y = stats.poisson.sf(r - 1, n*p)

    # If the first array isn't the correct one we test the others:
    while y < limit :
        n += 1

        if binom :
            y = stats.binom.sf(r - 1, n, p)
        if poisson:
            y = stats.poisson.sf(r - 1, n*p)

    # The previous loop stopped at n = the corret number, thereby we save:
    if y >= limit:
        x = n

    if plots :

        fig, ax = plt.subplots(figsize=(10, 8))
        ax2 = fig.add_axes([0.4, 0.2, 0.4, 0.4]) # add_axes([x0, y0, width, height])

        # We set an array for a zoomed plot as well:
        zoom_x = np.linspace(x-5, x+5, 10)

        if binom :
            yaxis = stats.binom.sf(r - 1, n_trials, p)
            y_value = stats.binom.sf(r - 1, x, p)
            zoom_y = stats.binom.sf(r - 1, zoom_x, p)
            Title = 'Cumulative Binomial Function'

        if poisson :
            yaxis = stats.poisson.sf(r - 1, n_trials*p)
            y_value = stats.poisson.sf(r - 1, x*p)
            zoom_y = stats.poisson.sf(r - 1, zoom_x*p)
            Title = 'Cumulative Poisson Function'

        ax.scatter(x+0.5, y_value,s=100, c='k', marker='o', zorder=3)
        ax.step(n_trials+0.5, yaxis, 'k')
        ax.axvspan(0, x, color = 'r', alpha = 0.2)
        ax.axvspan(x, len(n_trials), color = 'g', alpha = 0.2)
        ax.set(title=Title,
               xlabel='Number of trials',
               ylabel='Total Probability',
               xlim = [0, len(n_trials)]
              )

        ax2.step(zoom_x+0.5, zoom_y, 'k')
        ax2.axvspan(x-6, x, color = 'r', alpha = 0.5)
        ax2.axvspan(x, x+6, color = 'g', alpha = 0.5)
        ax2.scatter(x, y_value,s=100, c='k', marker='o', zorder=3)
        ax2.set(title = 'Zoomed:',
               xlim = [x-5, x+5])

        # We remove the ticks:
        ax2.tick_params(
            axis='both',
            which='both',
            left = False,
            right = False,
            bottom = False,
            top = False,
            labelleft = False,
            labelbottom = False)

        d = {'N:':   x,
             'Limit:':  limit,
            }

        text = nice_string_output(d, extra_spacing=2, decimals=2)
        add_text_to_ax(0.02, 0.95, text, ax, fontsize=18)

    return x

def chauvenet_iterative(data, crit = 1/2):

    """
    INPUT:
    data = 1d array with all data
    crit = float, with crit, default = 1/2, "equal quantity of points close to and far from mean"

    OUTPUT:
    data = 1d array with new cleaned data
    rejected_data = 1d array with rejected data
    """

    # Define an initial value of Prob, to create a while loop over
    prob = 0.01

    # Empty list to save the rejected data
    rejected_data = []

    # Start while loop which will rejects points under the criteria
    while prob < crit:

        # Calculate the mean of the sample, and the standard deviation with Bessels Correction
        mean, sigma = np.mean(data), np.std(data, ddof=1)

        # Calculate the absolute Z_scores
        Z_abs = abs(data - mean) / sigma

        # Find highest Z score - the point furthest away that we will investigate first
        index, Z = np.argmax(Z_abs), np.max(Z_abs)

        prob = len(data) * erfc(Z)

        # If the point is rejected, remove it from the data set and save it to rejected data
        if prob < crit:
            rejected_data = np.append( rejected_data, data[index] )
            data = np.delete( data, index )

    return data, rejected_data

def chauvenet_mask(data, crit=1/2):

    """
    INPUT:
    data = 1d array with data
    crit = chauvenets criterion
    """

    # Find mean and standard deviation
    mean, sigma = np.mean(data), np.std(data, ddof=1)

    # Define criterion
    criterion = crit

    # Z_score: number of standard deviations the point is away from the mean
    Z_abs = abs(data - mean) / sigma

    # Area under gaussian
    prob = len(data) * erfc(Z_abs)

    # Return a mask that is true for values we keep
    return prob > criterion

def sigma_cut(data, x):

    """
    INPUT:
    data = 1d array with all data
    x = float, number of sigma we want the cut off to be placed at

    OUTPUT:
    new_data = 1d array with accepted data
    rejected_data = 1d array with rejected data
    """

    # Find the standard deviation
    sigma = np.std(data, ddof=1)

    # Find interval we wish to keep
    low = mean - x*sigma
    high = mean + x*sigma

    # Cut the array to find new data
    new_data = data[ data >= low]
    new_data = new_data[ new_data <= high]

    # Collect rejected data
    rejected_data_low = data[ data < low ]
    rejected_data_high = data[ data > high ]

    rejected_data = np.concatenate( (rejected_data_low, rejected_data_high) )

    return new_data, rejected_data

def Sturges_bins(data):

    """
    INPUT:
    data = 1d array of all data

    OUTPUT:
    k = number of bins to use
    """

    # Number of data
    N = len(data)

    # Number of bins
    k = np.int( np.ceil( np.log2(N) ) + 1 )

    return k

def Doanes_bins(data):

    """
    INPUT:
    data = 1d array of all data

    OUTPUT:
    k = number of bins to use
    """

    # Number of data
    N = len(data)

    # Skewness
    g = 1 / (N * np.std(data)) * np.sum( (data - np.mean(data) )**3 )

    sig_g = np.sqrt( (6*(N-2)) / ((N+1)*(N+3)) )

    # Number of bins
    k = np.int( np.ceil( 1 + np.log2(N) + np.log2(1 + abs(g) / sig_g) ))

    return k

def Chi2_Gauss(x_data, N, mu, sigma, N_bins, plot=False, title=False):

    """
    IMPORTANT: It is not possible to give estimations for the Chi2 fit.
    INPUT:
    x = x-data, array-like
    N = number of experiments:
    mu = mean
    sigma = uncertainty of y-data, integer
    plots = whether to plot (True or False)
    title = title of plot in string: ex. 'title'

    """
    def Gauss(x, N, mu, sigma) :
        return N*binwidth * stats.norm.pdf(x, mu, sigma)
    counts, bin_edges = np.histogram(x_data, bins=N_bins)
    bin_centers = 0.5*(bin_edges[1:] + bin_edges[:-1])
    bin_width = bin_edges[1]-bin_edges[0]
    s_counts = np.sqrt(counts)

    x = bin_centers[counts>0]
    y = counts[counts>0]
    sy = s_counts[counts>0]

    chi2_gaussian = Chi2Regression(Gauss, x, y, sy)
    minuit_gaussian = Minuit(chi2_gaussian, pedantic=False, N=N_bins, mu=mu,
                             sigma=sigma)
    minuit_gaussian.migrad();

    print(minuit_gaussian.values['N'])
    Chi2 = minuit_gaussian.fval
    Ndof = len(x) - 3
    p = stats.chi2.sf(Chi2, Ndof)

    xmin = mu - 4*sigma
    xmax = mu + 4*sigma
    xaxis = np.linspace(xmin, xmax, 1000)

    if plot :
        fig, ax = plt.subplots(figsize=(12, 8))

        ax.hist(x_data, bins=N_bins, color='r', alpha=0.3)
        ax.hist(x_data, bins=N_bins, histtype='step', color='r')
        ax.errorbar(x, y, yerr=sy, fmt='ko', ecolor='k', elinewidth=1, capsize=1, capthick=1, label='Measurements')
        ax.plot(xaxis, Gauss(xaxis, *minuit_gaussian.args), color='k', linewidth=2)

        d = { 'Mean':   [minuit_gaussian.args[1],minuit_gaussian.args[2]],
             'N':   minuit_gaussian.args[0],
            'Chi2':     Chi2,
            'Ndof':     Ndof,
            'Prob':     p,
            }

        text = nice_string_output(d, extra_spacing=2, decimals=3)
        add_text_to_ax(0.02, 0.95, text, ax, fontsize=18)
        fig.suptitle(title, fontsize=25)

    """
    OUTPUT:
    Chi2 = Chi2 value
    p = Probabillity of this fit or worse
    minuit_fit.args = fitted value of Chi2
    Plot of the function, along with the Chi2 fit
    """

    return mean, sigma, Chi2, p

def KS_plotter(data, fit_function, args=(), zoom=True):
    """
    INPUT:
    data = empirical data to test against a fit
    fit_function = Scipy stats function that is fitted with in a string, fx 'norm'
    args = arguments to give fit function in default order

    OUTPUT:
    ks_stat = ks statistics value
    pval = p value of KS test
    """

    # Create figure
    fig, ax = plt.subplots(nrows=2, figsize=(12,8), sharex=True,
                       gridspec_kw = {'height_ratios': [1.5, 0.4], 'hspace': 0})

    # Plot data
    xaxis = np.sort(data)
    yaxis = np.arange(0, len(data), 1)
    ax[0].plot(xaxis, yaxis, color='green', label='Cumulative Data')

    # Plot fitted function's cdf
    cdf = getattr(stats.distributions, fit_function).cdf
    cdf_axis = len(data) * cdf(xaxis, *args)
    ax[0].plot(xaxis, cdf_axis, color='k', label = fit_function + '' + 'CDF')
    ax[0].legend(loc='upper left', prop={"size":14} )

    # Plot residuals
    resi = yaxis - cdf_axis
    ax[1].plot(xaxis, resi, color='k', linewidth=0.6, label = 'Residuals (Data $-$ CDF)')
    ax[1].hlines(0, xaxis[0], xaxis[-1], linestyle='dashed', color='green')

    ax[1].set_ylim(min(resi)+0.1*min(resi), max(resi)+0.1*max(resi))
    ax[1].legend(loc='lower right', prop={"size":12})

    # Plot zoom
    if zoom:

        # Create extra axis
        ax1 = fig.add_axes([0.65, 0.35, 0.2, 0.25]) # add_axes([x0, y0, width, height])
        ax1.plot(xaxis, yaxis, color='green')
        ax1.plot(xaxis, cdf_axis, color='k')

        # Find and supremum
        index = np.argmax(resi)

        # Adjust limits
        if resi[index] > 0: # Empirical data highest
            ymin, ymax = cdf_axis[index] - resi[index], yaxis[index] + resi[index]

        if resi[index] < 0: # Fit is highest
            ymin, ymax = yaxis[index] + resi[index], cdf_axis[index] - resi[index]

        xmin, xmax = xaxis[index] - 0.001 * xaxis[index], xaxis[index] + 0.001 * xaxis[index]

        ax1.set_xlim(xmin, xmax)
        ax1.set_ylim(ymin, ymax)

        # Mark supremum
        supremum = ConnectionPatch(xyA=(xaxis[index], cdf_axis[index]), xyB=(xaxis[index], yaxis[index]),
                                   coordsA=ax1.transData, arrowstyle='<->', color='b')
        fig.add_artist(supremum)

        ax1.set_title('$D_n = sup_x |F_n(x)-F(x)| $', color='b', fontsize=14)

        # Add zoom lines
        con1 = ConnectionPatch(xyA=(xmin, ymin), coordsA=ax[0].transData, xyB=(xmin, ymin),
                               coordsB=ax1.transData, alpha=0.3)
        con2 = ConnectionPatch(xyA=(xmax, ymax), coordsA=ax[0].transData, xyB=(xmax,ymax),
                               coordsB=ax1.transData, alpha=0.3)

        sq1 = ConnectionPatch(xyA=(xmin, ymin), xyB=(xmax, ymin), coordsA=ax[0].transData, alpha=0.3)
        sq2 = ConnectionPatch(xyA=(xmin, ymax), xyB=(xmax, ymax), coordsA=ax[0].transData, alpha=0.3)
        sq3 = ConnectionPatch(xyA=(xmin, ymin), xyB=(xmin, ymax), coordsA=ax[0].transData, alpha=0.3)
        sq4 = ConnectionPatch(xyA=(xmax, ymin), xyB=(xmax, ymax), coordsA=ax[0].transData, alpha=0.3)

        fig.add_artist(con1)
        fig.add_artist(con2)
        fig.add_artist(sq1)
        fig.add_artist(sq2)
        fig.add_artist(sq3)
        fig.add_artist(sq4)

    # Perform ks test
    ks_stat, pval = stats.kstest(data, fit_function, args=args )

    # Adding fit results to plot:
    d = {'KS stat':     ks_stat, 'Prob':     pval,}

    text = nice_string_output(d, extra_spacing=2, decimals=3)
    add_text_to_ax(0.02, 0.82, text, ax[0], fontsize=18)

    plt.show()

    return ks_stat, pval

def find_C(fx_expr, xmin, xmax, all_sol = False):

    """
    INPUT:
    fx_expr = the expression for f(x) in a string, fx '1+x' or 'C*(1+x)'
    xmin = the lower limit for which x is defined (closed interval)
    xmax = the upper limit for whcih x is defined (closed interval)

        if one of the limmits is C, this should be passed as xmin = symbols('C')

    all_sol = Print all solutions, also the ones discarded

    OUTPUT:
    C = the value of C in a sympy representation to obtain a normalised function
    """

    # Load the sympy variables we usually use for Monte Carlo
    x, f, C = symbols("x, f, C")

    # Sympify expression
    fx_expr = sympify(fx_expr)

    # Find the integrated function F
    integral = integrate(fx_expr, (x, xmin, xmax))

    # Find all solutions of C so the integral equals 1
    solutions = solve(integral - 1, C)

    # Find the real, positive solution
    for sol in solutions:
        if sol.is_real and sol.is_positive: C_val = sol

    # Raise a warning is solutions are discarded

    if not all_sol and len(solutions) > 1:
        print('Non-real and negative solutions were discarded. Set all_sol = True, to see all solutions')

    if all_sol and len(solutions) > 1:
        for expr in solutions: lprint(latex(Eq(symbols('C'), expr)))

    return C_val

def find_invF(fx_expr_no_C, C_val=None, xmin=-oo, all_sol = False ):

    """
    DISCLAIMER: This is not a pleasant function. We should really do this more
    generally by creating a function generator and another function using that function.
    This will allow us to define the sympy symbols before referencing them in the arguments.
    The arguments would thus be keywordarguments which can be read as a dictionary. See the error
    propagator for example. But im tired, so this is what you get.

    OBS If there are more real solutions to the inverse function (is it possible?)
    They will be overwritten when invF_expr = sol

    INPUT:
    fx_expr_noC = the expression for f(x) in a string, leaving out a potential C factor:
                  for example '5+x**2' even thought the right function is C*(5+x**2)

    C_val = the value of C, if theres is one in the function. If it is in the limits, ignore it.
    xmin = the lower limit where the function is defined
    all_sol = Print all solutions, also the ones discarded

    OUTPUT:
    inverse_function = python function representing the inverted function displayed in form f(u),
                       where the input u should be uniform numbers between 0 and 1.

    """

    # Gather expression if C is defined (I'm sorry)
    if C_val != None: fx_expr = str(C_val) + '*' + '(' + fx_expr_no_C + ')'
    if C_val == None: fx_expr = fx_expr_no_C

    # Load the sympy variables we usually use for Monte Carlo inverse function
    x, f, F, C, u = symbols("x, f, F, C, u")

    # Sympify expression for the function
    fx_expr = sympify(fx_expr)

    # Integrate the function from -inft to x, insert xmin if the function is not defined before
    F_expr = integrate( fx_expr, (x, xmin, x) )

    # Print the anti derivative
    lprint(latex(Eq(symbols('F(x)'), F_expr)))

    # Inverse function: set F(x) equal to u (uniform variable between 0,1) and isolate for x
    # Find all solutions to this
    solutions = solve(Eq(F_expr, u), x)

    # Find solution and display
    for sol in solutions:
        if 'I' not in str(sol):
            invF_expr = sol
            lprint(latex(Eq(symbols('F^{-1}(u)'), invF_expr)))

    # Raise a warning is solutions are discarded
    if not all_sol and len(solutions) > 1:
        print('Non-real and negative solutions were discarded. Set all_sol = True, to see all solutions')

    if all_sol and len(solutions) > 1:
        for expr in solutions: lprint(latex(Eq(symbols('C'), expr)))

    # Lambdify the inverse function
    inverse_function = lambdify(u, invF_expr)

    return inverse_function

def Acc_Rej(expr, N_points, N_bins, xmini, xmaxi, ymini, ymaxi, plot = False, title=False):

    """
    expr = defined function
    N_points = number of random numbers
    N_bins = number of bins

    Define the box:

    xmin = xmin for the box, often: 0
    xmax = xmax for the box, often: C found from the find_C function
    ymin = ymin for the box, often: 0
    ymax = ymax for the box, often: expr as a function of C, f(C)

    """

    xmax, xmin, ymax, ymin = float(xmaxi), float(xmini), float(ymaxi), float(ymini)

    # We start by creating the random numbers:

    #xmin, xmax, ymin, ymax = int(xmini), int(xmaxi), int(ymini), int(ymaxi)

    r = np.random
    r.seed(42)

    # We set the N_try to zero, which we loop over:
    N_try = 0

    # We create an empty array for which we add the accepted numbers:

    x_accepted = np.zeros(N_points)

    for i in range(N_points):

        while True: #While true loops means run until break:

            # Count the number of tries, to get efficiency/integral
            N_try += 1

            # Range that f(x) is defined/wanted in:
            x_test = r.uniform(xmin, xmax)

            # Upper bound for function values:
            y_test = r.uniform(ymin, ymax)

            if (y_test < expr(x_test)) :

                break

        x_accepted[i] = x_test

    # Efficiency
    eff = N_points / N_try

    # Error on efficiency (binomial!)
    eff_error = np.sqrt(eff * (1-eff) / N_try)

    # Integral
    integral =  eff * (xmax-xmin) * (ymax-ymin)

    # Error on integral
    integral_err = eff_error * (xmax-xmin) * (ymax-ymin)

    if plot :

        # We need to scale the expression to fit to the number of points:
        k = (xmax - xmin) / N_bins
        N = N_points * k

        # We plot over a simple linspace, thereby the function is not fitted
        x_axis = np.linspace(xmin, xmax, 1000)

        fig, ax = plt.subplots(figsize=(12, 10))

        # We plot the histrogram, and extract the values:
        hist = ax.hist(x_accepted, bins = N_bins, range=(xmin, xmax), label='Histogram', color='r', alpha=0.2)

        y, bin_edges, _ = hist
        x = 0.5*(bin_edges[1:] + bin_edges[:-1])
        sy = np.sqrt(y)

        ax.errorbar(x, y, yerr=sy, fmt='ko', ecolor='k', elinewidth=1, capsize=1, capthick=1, label='Measurements')
        ax.hist(x_accepted, bins = N_bins, range=(xmin, xmax), histtype='step',   color='r', alpha=1)
        ax.set(xlabel="Generated numbers", ylabel="Frequency", xlim=(xmin-0.1, xmax+0.1));
        ax.plot(x_axis, N * expr(x_axis) , 'k-', label='Function (not fitted)', linewidth=2)

        # Define figure text
        d = {'Entries': len(x_accepted),
             'Mean': x_accepted.mean(),
             'Std':  x_accepted.std(ddof=1),
            }

        # Plot figure text
        text = nice_string_output(d, extra_spacing=2, decimals=3)
        add_text_to_ax(0.05, 0.75, text, ax, fontsize=16)

        # Add legend
        ax.legend(loc='best')
        fig.tight_layout()

        """
        OUTPUT:
        Efficiency
        Error Efficiency

        Value under integral
        Error integral
        """
    return x_accepted, [eff, eff_error], [integral, integral_err]

# We define a function that returns the relative error:
def relative_err(N_point, expr, expr_chi):

    N_bins = np.int( np.ceil( np.log2(N_point) ) + 1 )
    xmax_1 = 10
    xmin_1 = 1

    x_values, _, _ = Acc_Rej(expr, N_point, N_bins, xmin_1, xmax_1, f(0), f(10), plot= False, title=False)

    hist_data = np.histogram(x_values, bins=N_bins)

    y, bin_edges = hist_data
    x = 0.5*(bin_edges[1:] + bin_edges[:-1])
    binwidth = bin_edges[1] - bin_edges[0]
    sy = np.sqrt(y)

    N_new = len(x)
    xmin, xmax = x[0], x[-1]

    fit = Chi2Regression(expr_chi, x, y, sy=sy, bound=(1, 10))
    minuit_fit = Minuit(fit, print_level=0, pedantic=False)
    minuit_fit.migrad();

    # Defining the relative error:

    C1 =  minuit_fit.errors['c_1']/minuit_fit.values['c_1']

    C2 =  minuit_fit.errors['c_2']/minuit_fit.values['c_2']
    return C1, C2

def get_corr(x_data, y_data):
    """
    Calculates the linear correlation of two variables, given a data set with values for each variable

    INPUT:
    x_data = 1d arraylike with all x values of a data set where x is a variable
    y_data = 1d arraylike with all corresponding y values of a data set

    OUTPUT:
    rho_xy = the linear correlation
    """

    # Covariance matrix
    cov_mat = np.cov(x_data, y_data)

    # extract covariance and variance for each variable
    cov, var_x, var_y = cov_mat[0,1], cov_mat[0,0], cov_mat[1,1]

    # Find correlation cov / sigma_x * sigma_y. Remember sigma = sqrt(Var)
    rho_xy = cov / (np.sqrt(var_x)*np.sqrt(var_y))

    return rho_xy

def error_rates(species_A, species_B, cut, N_bins = 50, plot=True, alp_coord=(0.1, 0.52), bet_coord=(0.1, 0.52), labelA = 'Species A', labelB='Species B'):
    """
    INPUT:
    species_A : 1d arraylike containing data for one variable of species A.
                Species A should be below the cut.
    species_B : 1d arraylike containing data for same variable of species B.
                Species B should be above or equal to the cut.
    cut = float, defining the cut
    N_bins = number of bins to plot for each species
    plot = option to plot or not
    alp_coord = coordinates to place the axtext alpha on the form (x,y) where 0 < x,y < 1
    bet_coord = coordinates to place the axtext beta on the form (x,y) where 0 < x,y < 1

    OUTPUT:
    alp = type I error rate
    bet = type II error rate
    """

    # Separate data acoording to cut
    A_sup_cut = species_A[species_A >= cut]
    B_sub_cut = species_B[species_B < cut]

    # Calculate error rates
    alp = len(B_sub_cut) / len(species_B)
    bet = len(A_sup_cut) / len(species_A)

    # Create figure
    if plot:
        fig, ax = plt.subplots(ncols=2, figsize=(12,6))

        # Define range
        rang = ( np.min(np.concatenate((species_A, species_B))), np.max( np.concatenate((species_A, species_B))) )

        # Type I errors (alpha)---------------------------------------------------------------------------
        countA, _, _ = ax[0].hist(species_A, bins=N_bins, range=rang, histtype='step', color='red', linewidth=2, label=labelA)
        countB, _, _ = ax[0].hist(species_B, bins=N_bins, range=rang, histtype='step', color='blue', linewidth=2, label=labelB)

        ax[0].vlines(cut, 0, np.max((countA, countB))+10, color='k', linestyle='dashed', label='Cut=9$\mu$m')

        # Mark the area under
        ax[0].hist(B_sub_cut, bins=N_bins, range=rang, histtype='stepfilled', color='blue', alpha=0.5,linewidth=2, label='Beta')

        d1 = {'Alpha':     alp}
        text1 = AppStars.nice_string_output(d1, extra_spacing=2, decimals=3)
        AppStars.add_text_to_ax(*alp_coord, text1, ax[0], fontsize=14, color='blue')

        ax[0].set_title('Type I Error', fontsize=14)
        ax[0].legend()

        # Type II errors (beta) ---------------------------------------------------------------------------
        ax[1].hist(species_A, bins=N_bins, range=rang, histtype='step', color='red', linewidth=2, label=labelA)
        ax[1].hist(species_B, bins=N_bins, range=rang, histtype='step', color='blue', linewidth=2, label=labelB)

        ax[1].vlines(cut, 0, np.max((countA, countB))+10, color='k', linestyle='dashed', label=f'Cut={cut}')

        # Mark the area under
        ax[1].hist(A_sup_cut, bins=N_bins, range=rang, histtype='stepfilled', color='red', alpha=0.5, linewidth=2, label='Alpha')

        d2 = {'Beta':     bet}
        text2 = AppStars.nice_string_output(d2, extra_spacing=2, decimals=3)
        AppStars.add_text_to_ax(*bet_coord, text2, ax[1], fontsize=14, color='red')

        ax[1].set_title('Type II Error', fontsize=14)
        ax[1].legend()

        plt.show()

    return alp, bet


import sqlite3

def buscarElementoEnLista(lista, valor):
    t = ''
    for tupla in lista:
        for elemento in tupla:
            if elemento == valor:
                t = tupla
    return t

def pedirProducto():
    nombre = input("Ingresa el NOMBRE del producto: -> ")
    precio = input("Ingresa el PRECIO del producto: -> ")
    seccion = input("Ingresa la SECCION del producto: -> ")
    return nombre, precio, seccion

def dbProcesarConsulta(consulta, tipo):
    resultado = ''
    miConexion = sqlite3.connect("GestionProductos.db")
    miCursor = miConexion.cursor()

    if tipo == "select":
        miCursor.execute(consulta)
        resultado = miCursor.fetchall()
    if tipo == "insert" or tipo == "delete" or tipo == "update":
        miCursor.execute(consulta)
        resultado = True

    miConexion.commit()
    miConexion.close()

    return resultado

def listalProductos():
    consulta = "SELECT * FROM PRODUCTOS"
    listaproductos = dbProcesarConsulta(consulta, tipo="select")
    print("----------------")
    for producto in listaproductos:
        print(f"{producto[0]} - {producto[1]} - {producto[2]}")
    print("----------------")
    return listaproductos

def agregarProducto():
    #     miCursor.execute("INSERT INTO productos VALUES ('Balon', 15, 'Deportes')")
    n, p, s = pedirProducto()
    consulta = "INSERT INTO productos VALUES (null,'{}', {}, '{}')".format(n, int(p), s)
    estado = dbProcesarConsulta(consulta, tipo="insert")
    return estado  # True en caso de  que se pudo hacer la consulta.

def eliminarProducto():
    id = int(input("Ingresa el ID del producto a eliminar - > "))
    consulta = f"DELETE FROM PRODUCTOS WHERE ID = {id}"
    estado = dbProcesarConsulta(consulta, tipo="delete")
    return estado  # True en caso de  que se pudo hacer la consulta.

def modificarProducto():

    productos = listalProductos()
    id = int(input("Ingresa el ID del producto a modificar - > "))

    global tupla
    tupla = buscarElementoEnLista(productos, id)
    while tupla[0] != id:
        print("Ese ID no es valido - reintentar.. ")
        id = int(input("Ingresa el ID del producto a modificar - > "))

        tupla = buscarElementoEnLista(productos, id)

    n, p, s = pedirProducto() # UPDATE PRODUCTOS SET PRECIO=35 WHERE NOMBRE_ARTICULO='pelota'

    if n == '':
        n = tupla[1]
    if p == '':
        p = tupla[2]
    else:
        p = int(p)
    if s == '':
        s = tupla[2]

    consulta = "UPDATE PRODUCTOS SET NOMBRE_ARTICULO='{}', PRECIO={}, SECCION='{}' WHERE ID={}".format(n, p, s, id)

    #print(consulta)

    estado = dbProcesarConsulta(consulta, tipo="update")

    return estado # True en caso de  que se pudo hacer la consulta.

menu = '''
    . : MENU : .
1 - Listar elementos
2 - Agregar elementos
3 - Borrar elementos
4 - Modificar elementos
-----------------------
Trabajo con archivos (XD)
8 - none
9 - none
0 - Salir del programa.
'''

while True:
    print(menu)
    op = input("Ingresa la opción - > ")

    if op == '0':
        print("Saliendo del programa ... ")
        break
    elif op == '1':
        listalProductos()
    elif op == '2':
        if agregarProducto():
            print("Se agregó el producto en la base de datos.")
    elif op == '3':
        if eliminarProducto():
            print("Se eliminó el producto de la base de datos")
    elif op == '4':
        if modificarProducto():
            print("Se modifico  el producto de la base de datos")
    else:
        print("Opcion no valida.")


"""This file contains implementation to ingest Dataminr RTAP alert data into sentinel."""
import inspect
from .sentinel import MicrosoftSentinel
from shared_code.consts import (
    LOGS_STARTS_WITH,
    ANOMALY_LOG_TYPE,
    RANSOMWARE_LOG_TYPE,
    THREATHUNT_LOG_TYPE,
)
from shared_code.rubrik_exception import RubrikException
from shared_code.logger import applogger

class Rubrik:
    """This class contains methods to get data from request body pushed via Rubrik Webhook and ingest into Sentinel."""

    def __init__(self) -> None:
        """Initialize instance variables for class."""
        self.logs_starts_with = LOGS_STARTS_WITH
        self.microsoftsentinel = MicrosoftSentinel()
        self.error_logs = "{}(method={}) {}"
        self.check_environment_var_existance()

    def check_environment_var_existance(self):
        """To verify that all required environment variables exist.

        Raises:
            RubrikException: raise exception if any of the required environment variable is not set.
        """
        __method_name = inspect.currentframe().f_code.co_name
        env_var = [
            {"Anomalies_table_name": ANOMALY_LOG_TYPE},
            {"RansomwareAnalysis_table_name": RANSOMWARE_LOG_TYPE},
            {"ThreatHunts_table_name": THREATHUNT_LOG_TYPE},
        ]
        try:
            applogger.debug(
                "{}(method={}) Checking environment variables are exist or not.".format(
                    self.logs_starts_with, __method_name
                )
            )
            for i in env_var:
                key, val = next(iter(i.items()))
                if val is None:
                    raise RubrikException(
                        "{} is not set in the environment please set the environment variable.".format(
                            key
                        )
                    )
            applogger.debug(
                "{}(method={}) All custom environment variables exist.".format(
                    self.logs_starts_with, __method_name
                )
            )
        except RubrikException as err:
            applogger.error(
                "{}".format(
                    self.error_logs.format(self.logs_starts_with, __method_name, err)
                )
            )
            raise RubrikException(err)
        except Exception as err:
            applogger.error(
                "{}".format(
                    self.error_logs.format(self.logs_starts_with, __method_name, err)
                )
            )
            raise RubrikException(err)

    def post_data_to_sentinel(self, data):
        """To post data received via Rubrik Webhook into Sentinel.

        Args:
            data (dict): data received via Rubrik Webhook.
        """
        __method_name = inspect.currentframe().f_code.co_name
        try:
            sentinel_obj = MicrosoftSentinel()
            body = data.get("data")
            log_type = data.get("log_type")
            sentinel_obj.post_data(body, log_type)
        except Exception as err:
            applogger.error(
                "{}".format(
                    self.error_logs.format(self.logs_starts_with, __method_name, err)
                )
            )
            raise RubrikException(err)