import os, re, math, sys
from collections import defaultdict

VCF_CONTIG_PATT = re.compile('ID=(\w+),length=(\d+)')

PROG_NAME = 'Hycco'

DESCRIPTION = 'Hycco is an HMM based method to estimate hybrid chromosomal crossover points using distinguising SNPs from two parental genotypes'

FILE_TAG = 'crossover_regions'

DEFAULT_BIN_SIZE = 10000

DEFAULT_QUALITY = 100.0

DEFAULT_MIN_CHROMO_SIZE = 1.0

DEFAULT_NUM_ITER = 400

def info(msg, prefix='INFO'):
  print('%8s : %s' % (prefix, msg))

def warn(msg, prefix='WARNING'):
  print('%8s : %s' % (prefix, msg))

def fatal(msg, prefix='%s FAILURE' % PROG_NAME):
  print('%8s : %s' % (prefix, msg))
  sys.exit(0)

def check_file_problems(file_path):

  problem = None

  if not os.path.exists(file_path):
    problem = 'File "%s" does not exist'
    return problem % file_path

  if not os.path.isfile(file_path):
    problem = 'Location "%s" is not a regular file'
    return problem % file_path

  if os.stat(file_path).st_size == 0:
    problem = 'File "%s" is of zero size '
    return problem % file_path

  if not os.access(file_path, os.R_OK):
    problem = 'File "%s" is not readable'
    return problem % file_path

  return problem

def test_imports():

  critical = False

  try:
    from numpy import array
  except ImportError:
    critical = True
    warn('Critical module "numpy" is not installed or accessible')

  try:
    from sklearn import cluster
  except ImportError:
    critical = True
    warn('Critical module "sklearn" is not installed or accessible')

  try:
    from hmmlearn import hmm
  except ImportError:
    critical = True
    warn('Critical module "hmmlearn" is not installed or accessible')

  try:
    from matplotlib import pyplot

  except ImportError:
    warn('Module "matplotlib" is not installed or accessible. Graphing option is not available.')

  if critical:
    fatal('Exiting because critial Python modules are not available')

test_imports()

import numpy as np

def read_vcf(file_path, min_qual=100):
  """
  Read VCF file to get SNPs with a given minimum quality.
  Reurns chromsome sizes and SPN positions with qualities, keyed by chromosome
  """

  if file_path.lower().endswith('.gz'):
    import gzip
    file_obj = gzip.open(file_path, 'rt')
  else:
    file_obj = open(file_path)

  chromo_dict = {}
  var_dict = defaultdict(list)

  for line in file_obj:
    if line[:9] == '##contig=':
      match = VCF_CONTIG_PATT.search(line)

      if match:
        chromo = match.group(1)
        size = int(match.group(2))
        chromo_dict[chromo] = size

    elif line[0] == '#':
      continue

    else:
      chromo, pos, _id, ref, alt, qual, filt, info, fmt, xgb3 = line.split()
      qual = float(qual)

      if len(ref) == 1 and len(alt) == 1:
        if qual >= min_qual:
          var_dict[chromo].append((int(pos), qual))

  file_obj.close()

  # VCF may not be sorted
  sort_var_dict = {}
  for chromo in var_dict:
    sort_var_dict[chromo] = sorted(var_dict[chromo])

  return chromo_dict, sort_var_dict

def get_contig_data(chromo_dict, var_dict, bin_size=1000):
  """
  Given a dictionary of chromosome sizes, bins the log_10 scores of variant
  positions into a contiguous array for each chromosome. Returns a list
  of lists, one for each chromosome in sorted order.
  """

  contigs = []
  bs = float(bin_size)

  for chromo in sorted(chromo_dict):
    size = chromo_dict[chromo]
    n = int(math.ceil(size/bs)) + 1
    contig = np.zeros(n, float)

    for pos, qual in var_dict.get(chromo, []):
      i = int(pos/bin_size)
      w = (pos - (i*bin_size))/bs
      q =  np.log10(1.0 + qual)

      contig[i] += (1.0-w) * q
      contig[i+1] += w * q

    contigs.append(contig)

  return contigs

def get_training_data(contig_data1, contig_data2):
  """
  Joins the TMM training data for contiguous SNP scores derived from
  different genome builds.
  """

  data = []
  sizes = []

  for i, contig1 in enumerate(contig_data1):
    contig2 = contig_data2[i]

    row = list(zip(contig1, contig2))
    sizes.append(len(row))
    data.append(np.array(row))

  data = np.concatenate(data)

  return data, sizes

def train_hybrid_crossover_hmm(vcf_paths_pairs, text_labels=('A','B'), out_dir='', bin_size=10000, min_qual=100,
                              min_chromo_size=1e6, num_hmm_iter=400, plot_graphs=False, covariance_type='diag'):
  """
  Main function to train the HMM and plot the results
  """

  from hmmlearn import hmm

  # This is simply to remove worrysome messages which ough to be addressed in newer hmmlearn versions
  from sklearn import warnings
  def nullfunc(*args, **kw):
    pass

  warnings.warn = nullfunc

  chromos = set()
  var_pairs = []
  chromo_dicts = []
  n_states=2

  # Read the VCF data and chromosome sizes

  for vcf_path_a, vcf_path_b in vcf_paths_pairs:
    chromo_dict_a, var_dict_a = read_vcf(vcf_path_a, min_qual)
    chromo_dict_b, var_dict_b = read_vcf(vcf_path_b, min_qual)

    chromos.update(chromo_dict_a.keys())
    chromos.update(chromo_dict_b.keys())

    var_pairs.append((var_dict_a, var_dict_b))
    chromo_dicts += [chromo_dict_a, chromo_dict_b]

  # Collate chromosome sizes, talking the largest,
  # just in case there are any discrepencies and
  # ignoring any that are too small to bother with

  chromo_dict = {}
  for chromo in chromos:
    size = max([cd.get(chromo, 0) for cd in chromo_dicts])

    if size >= min_chromo_size:
      chromo_dict[chromo] = size

  chromos = sorted(chromo_dict)

  # Look through variant call pairs for each strain

  if plot_graphs:
    fig, axarr = plt.subplots(len(chromos), len(var_pairs))
    title_text = 'Hybrid genome HMM states. Bin size = {:,} Min qual = {}'
    fig.suptitle(title_text.format(bin_size, min_qual))

  n_cols = len(var_pairs)

  head_1 = '#HMM params - bin_size:%d min_qual:%d min_chromo_size:%d num_hmm_iter:%d\n'
  head_2 = '#cols - chr\thaplotype\tregion_size\tregion_start\tregion_end\tfirst_SNP\tlast_SNP\tbin_start\tbin_end\n'

  for col, (var_dict_a, var_dict_b) in enumerate(var_pairs):
    file_name = '%s_%s.tsv' % (text_labels[col], FILE_TAG)
    file_path = os.path.join(out_dir, file_name)
    file_obj = open(file_path, 'w')
    write = file_obj.write

    write(head_1 % (bin_size, min_qual, min_chromo_size, num_hmm_iter))
    write(head_2)

    contig_data_a = get_contig_data(chromo_dict, var_dict_a, bin_size)
    contig_data_b = get_contig_data(chromo_dict, var_dict_b, bin_size)
    in_data, sizes = get_training_data(contig_data_a, contig_data_b)

    # Setup an HMM object
    model = hmm.GaussianHMM(n_components=n_states, covariance_type=covariance_type,
                            n_iter=num_hmm_iter)

    # Run Baum-Welch to lear the HMM probabilities

    model.fit(in_data, sizes)

    mv = in_data.max()
    i = 0

    for row, chromo in enumerate(chromos):
      m = sizes[row]
      chrom_data = in_data[i:i+m]
      i += m

      # Run Forward-Backward to get state probabilities at each point
      probs = model.predict_proba(chrom_data, [m])

      # The order of state labels is arbitrary, so use a dot product to
      # deduce which state best matches the first genome
      dp1 = np.dot(probs[:,0], chrom_data[:,0])
      dp2 = np.dot(probs[:,0], chrom_data[:,1])

      if dp2 > dp1:
        probs_a = probs[:,1]
        probs_b = probs[:,0]
      else:
        probs_a = probs[:,0]
        probs_b = probs[:,1]

      # Create chromosome regios of contiguous state according to which of
      # the probabilities for the binned regions was higest

      prev_state = 0
      region_start = 0
      chromo_regions = []
      for j in range(m):
        pos = j * bin_size

        if probs_a[j] > probs_b[j]:
          state = 'A'
        elif probs_a[j] < probs_b[j]:
          state = 'B'
        else:
          state = ''

        if state != prev_state:
          if prev_state:
            chromo_regions.append((region_start, pos, prev_state))
          region_start = pos

        prev_state = state

      # Last region goes to the chromosome end
      if state:
        chromo_regions.append((region_start, min(pos, chromo_dict[chromo]), prev_state))

      # Refine region edges according to precise SNP positions
      # which could be before or after end of binned region

      # Remove SNPs common to both genotypes
      pos_counts = defaultdict(int)
      for pos, qual in var_dict_a[chromo]:
        pos_counts[pos] += 1

      for pos, qual in var_dict_b[chromo]:
        pos_counts[pos] += 1

      # Get sorted positions (and corresponding states) of distinguishing SNPs

      vars_state_pos  = [(pos, 'A') for pos, qual in var_dict_a[chromo] if pos_counts[pos] < 2]
      vars_state_pos += [(pos, 'B') for pos, qual in var_dict_b[chromo] if pos_counts[pos] < 2]
      vars_state_pos.sort()

      var_pos, var_states = zip(*vars_state_pos)
      var_pos = np.array(var_pos)
      n_var = len(var_pos)

      for start, end, state in chromo_regions:

        # Find transitions from A/B genotypes SNPs working away from region edge
        # Report terminal snips and mid-way between transitions, where possible

        # Refine beginning

        idx_left = idx_right = np.abs(var_pos-start).argmin() # Closest SNP to bin boundary

        while (idx_right < n_var-1) and (var_states[idx_right] != state): # Find next matching SNP
          idx_right += 1

        while (idx_left >= 0) and (var_states[idx_left] == state): # Find prev mismatching SNP
          idx_left -= 1

        vp1 = var_pos[idx_right]

        if vp1 > end:
          msg = 'No SNPs for HMM state "%s" found in chromosome region %s:%d-%d. '
          msg += 'Probably the HMM was not able to separate states in the data as expected'
          warn(msg)
          vp1 = start

        if idx_left < 0: # Off the chromosome start
          rp1 = 0
        else:
          rp1 = int((var_pos[idx_left] + vp1)/2)

        # Refine end

        idx_left = idx_right = np.abs(var_pos-end).argmin() # Closest SNP to bin boundary

        while (idx_left >= 0) and (var_states[idx_left] != state): # Find prev matching SNP
          idx_left -= 1

        while (idx_right < n_var) and (var_states[idx_right] == state): # Find next mismatching SNP
          idx_right += 1

        vp2 = var_pos[idx_left]

        if vp2 < start:
          vp2 = end

        if idx_right < n_var:
          rp2 = int((vp2 + var_pos[idx_right])/2)
        else: # Off the chromosome end
          rp2 = end

        # Chromosome, state code, region start, region end, region length,
        #   first matching var position, last matching var pos
        line_data = (chromo, state, rp2-rp1, rp1, rp2, vp1, vp2, start, end)
        line = '%s\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n' % line_data
        write(line)

      if plot_graphs:
        # Plot first probabilities at a resonable scale
        probs = probs_a * 0.75 * mv

        # X valuys for plot in Megabases
        x_vals = np.array(range(len(chrom_data))) * bin_size / 1e6
        nx = x_vals[-1]

        # Plot the lines

        if n_cols > 1:
          ax = axarr[row, col]
        else:
          ax = axarr[row]

        ax.plot(x_vals, chrom_data[:,0], color='#FF4000', alpha=0.4, linewidth=1.5, label='Genome A SNPs')
        ax.plot(x_vals, chrom_data[:,1], color='#0080FF', alpha=0.4, linewidth=1.5, label='Genome B SNPs')
        ax.plot(x_vals, probs[:], color='#808080', alpha=0.75, linewidth=1.0, label='State A probability', linestyle='-')

        # Titles axes and labels at the aprropriate spots
        dx =  bin_size / 1e6

        if row == 0:
          ax.set_title(text_labels[col])

        ax.set_xlim((-dx, nx+dx))
        ax.set_ylim((0, 1.1*mv))
        ax.set_xlabel('Chr %s Position (Mb)' % chromo, fontsize=11)
        ax.axhline(0, -dx, nx+dx, color='#808080', alpha=0.5)

        if col == 0:
          ax.set_ylabel('$\Sigma[log_{10}(1+qual)]$')

        if row == 0:
          ax.legend(fontsize=11, frameon=False, ncol=3)

    file_obj.close()

    info('Output data written to %s' % file_path)

  if plot_graphs:
    plt.show()

if __name__ == '__main__':

  from argparse import ArgumentParser

  epilog = 'For further help email tstevens@mrc-lmb.cam.ac.uk or garethb@mrc-lmb.cam.ac.uk'

  arg_parse = ArgumentParser(prog=PROG_NAME, description=DESCRIPTION,
                             epilog=epilog, prefix_chars='-', add_help=True)

  arg_parse.add_argument(metavar='VCF_FILE', nargs='+', dest='i',
                         help='Input VCF format files containing variant calls for each parental genotype/strain. Files should be listed sequentially in pair order. Inputs may be gzipped (this is assumed by a .gz file extension).')

  arg_parse.add_argument('-b', default=DEFAULT_BIN_SIZE, type=int, metavar='BIN_SIZE',
                         help='Binned analysis region size (in bp) for defining HMM chromosome segments (). Default: %d bp.' % DEFAULT_BIN_SIZE)

  arg_parse.add_argument('-g', default=False, action='store_true',
                         help='Specifies that graphical output will be displayed for each VCF pair using matplotlib')

  arg_parse.add_argument('-m', default=DEFAULT_MIN_CHROMO_SIZE, type=float, metavar='MIN_CHROMO_SIZE',
                         help='Minimum length (in Mb) required for a contig/chromosome (as described in VCF header) to be analysed. Default: %.2f Mb.' % DEFAULT_MIN_CHROMO_SIZE)

  arg_parse.add_argument('-n', default=DEFAULT_NUM_ITER, type=int, metavar='NUM_HMM_ITER',
                         help='Number of iterations to perform when estimating Gaussian HMM probabilities using the Baum-Welch method. Default: %d' % DEFAULT_NUM_ITER)

  arg_parse.add_argument('-o', metavar='OUT_DIR',
                         help='Optional output directory for writing results. Defaults to the current working directory.')

  arg_parse.add_argument('-q', default=DEFAULT_QUALITY, type=float, metavar='',
                         help='Minimum quality (phred-scale) for accepting a SNP call, as described in the VCF data lines. Default: %.2f' % DEFAULT_QUALITY)

  arg_parse.add_argument('-s', default=0, type=int, metavar='RANDOM_SEED',
                         help='Optional random seed value, i.e. to make repeat calculations deterministic.')

  arg_parse.add_argument('-t', nargs='+', metavar='TEXT_LABEL',
                         help='Optional text labels to describe each input pair, which are used to name output files as {NAME}_%s.tsv, in the same order as the input VCF file pairs.' % FILE_TAG)

  args = vars(arg_parse.parse_args(sys.argv[1:]))

  vcf_paths       = args['i']
  plot_graphs     = args['g']
  ran_seed        = args['s']
  out_dir         = args['o'] or './'
  text_labels     = args['t']
  bin_size        = args['b']
  min_qual        = args['q']
  min_chromo_size = int(args['m'] * 1e6)
  num_hmm_iter    = args['n']

  try:
    from matplotlib import pyplot as plt
  except ImportError:
    plot_graphs = False

  if ran_seed:
    np.random.seed(ran_seed)

  n_paths = len(vcf_paths)

  if n_paths < 2:
    fatal('At least two VCF file paths must be specified')

  if n_paths % 2 == 1:
    fatal('An even number of VCF paths (i.e. pairs of files) must be input. %d paths were specified' % n_paths)

  n_pairs = n_paths/2

  if text_labels:
    text_labels = list(text_labels)
  else:
    text_labels = []

  while len(text_labels) < n_pairs:
    label = 'pair_%d' % (1+len(text_labels))
    text_labels.append(label)

  if len(text_labels) > n_pairs:
    warn('Number of input text labels (%d) greater than the number of input pairs (%s)' % (len(text_labels), n_pairs))
    text_labels = text_labels[:n_pairs]

  abs_path = os.path.abspath(out_dir)
  if not os.path.exists(abs_path):
    fatal('Output directory "%d" does not exist')

  if not os.path.isdir(abs_path):
    fatal('Output path "%d" is not a directory')

  for vcf_path in vcf_paths:
    problem = check_file_problems(vcf_path)

    if problem:
      fatal(problem)

  vcf_paths_pairs = [(vcf_paths[i], vcf_paths[i+1]) for i in range(0, n_paths, 2)]

  train_hybrid_crossover_hmm(vcf_paths_pairs, text_labels, out_dir, bin_size,
                             min_qual, min_chromo_size, num_hmm_iter, plot_graphs)

  # Example ./hycco xgb3_vs12_clean.vcf.gz xgb3_vs13_clean.vcf.gz


# Student Virtual Assistant

# Libraries for accessing Google Scholar and scheduling reminders
import webbrowser
import schedule
import time

# Function to access Google Scholar
def search_scholar(query):
  webbrowser.open(f"https://scholar.google.com/scholar?q={query}")

# Function to input schedule
def input_schedule():
  schedule = {}
  while True:
    class_name = input("Enter class name (or type 'done' if finished): ")
    if class_name == "done":
      break
    class_time = input("Enter class time (e.g. 9:00 AM): ")
    schedule[class_name] = class_time
  return schedule

# Function to input assignments
def input_assignments():
  assignments = []
  while True:
    assignment = input("Enter assignment (or type 'done' if finished): ")
    if assignment == "done":
      break
    assignments.append(assignment)
  return assignments

# Function to remind student to hydrate
def remind_hydrate():
  print("Don't forget to drink water and stay hydrated!")

# Main function to run the virtual assistant
def run_assistant():
  print("Welcome to the Student Virtual Assistant")

  # Input schedule
  print("Please enter your schedule:")
  schedule = input_schedule()
  print(f"Your schedule: {schedule}")

  # Input assignments
  print("Please enter your assignments:")
  assignments = input_assignments()
  print(f"Your assignments: {assignments}")

  # Access Google Scholar
  query = input("What would you like to search on Google Scholar? ")
  search_scholar(query)

  # Schedule reminders to hydrate
  schedule.every(1).hours.do(remind_hydrate)

  # Start reminders
  while True:
    schedule.run_pending()
    time.sleep(1)

# Run the virtual assistant
if __name__ == "__main__":
  run_assistant()


#!/usr/bin/python

import csv
import json
import pprint
import re
import sys

def replace_if_not_empty(dict, key, value):
    if key not in dict or not dict[key]:
        dict[key] = value

def to_float_or_none(value):
    # lmao lazy
    try:
        return float(value)
    except ValueError:
        return None

def replace_slashes(str):
    return str.replace("/", "\\") if str else None

def strip_or_none(str):
    try:
        return str.strip() if str.strip() else None
    except:
        return None

def write_json(dic, file_name):
    print(f"saving {file_name}")
    with open(file=file_name, mode="w") as f:
        json.dump(dic, f, indent=2)
        f.write("\n")

def main(tsv_name: str):
    print(f"reading {tsv_name}")

    with open(file=tsv_name) as tsv_file:
        # skip first 2 lines
        tsv_file.readline()
        tsv_file.readline()

        # convert file to array of dicts
        # not efficient but good for reuse
        rows = list(csv.DictReader(tsv_file, delimiter="\t"))

        extract_keyboards(rows)
        extract_mousepads(rows)
        extract_mice(rows)
        extract_users(rows)

def extract_keyboards(rows):
    kbs = {}
    for row in rows:
        kb_model = replace_slashes(strip_or_none(row["Keyboard Model"]))
        if not kb_model:
            continue

        if kb_model not in kbs or not kbs[kb_model]:
            kb = {}
        else:
            kb = kbs[kb_model]

        # set keyboard switch
        replace_if_not_empty(kb, "switch", strip_or_none(row["Key Switch"]))
        kbs[kb_model] = kb

    # pprint.PrettyPrinter().pprint(kbs)
    write_json(kbs, "keyboards.json")

def extract_mousepads(rows):
    mps = {}
    for row in rows:
        mp = replace_slashes(strip_or_none(row["Mousepad"]))
        if not mp:
            continue
        if mp not in mps or not mps[mp]:
            mps[mp] = {}

    # pprint.PrettyPrinter().pprint(mps)
    write_json(mps, "mousepads.json")

def extract_mice(rows):
    mice = {}

    # iterate through rows
    for row in rows:
        mm = replace_slashes(strip_or_none(row["Mouse Model"]))
        # skip row
        if not mm:
            continue

        # get mouse model
        if mm not in mice or not mice[mm]:
            mouse = {}
        else:
            mouse = mice[mm]

        # update mouse model
        # using split because sometimes there is a range of values
        # im lazy so i just take first value
        replace_if_not_empty(
            mouse, "sensor", row["Sensor"] if row["Sensor"] else None)
        replace_if_not_empty(mouse, "weight", to_float_or_none(
            row["Weight"].split("g")[0]))
        replace_if_not_empty(mouse, "length", to_float_or_none(
            row["Length"].split("m")[0]))
        replace_if_not_empty(mouse, "width", to_float_or_none(
            row["Width"].split("m")[0]))
        replace_if_not_empty(mouse, "height", to_float_or_none(
            row["Height"].split("m")[0]))
        replace_if_not_empty(
            mouse, "switch", row["Mouse Switch"] if row["Mouse Switch"] else None)

        # set mouse model
        mice[mm] = mouse

    # pprint.PrettyPrinter().pprint(mice)
    write_json(mice, "mice.json")

def extract_users(rows):
    status = "Mouse"
    users = {}

    # iterate through rows
    for row in rows:
        if row["Rank"].startswith("Notable"):
            status = "Notable"
        elif row["Rank"].startswith("Traitors"):
            status = "Traitors"
        elif row["Rank"].startswith("Banned"):
            break

        # skip notable mentions
        if status == "Notable":
            continue

        # skip row if username is not well-formatted
        pattern = "=HYPERLINK\\(\"https:\\/\\/osu\\.ppy\\.sh\\/u\\/(\\d+)\",\"(\\w+)\"\\)"
        result = re.search(pattern, row["Name"])
        if not result or not result.group(1) or not result.group(2):
            continue

        # extract user info
        userID = result.group(1)
        userName = result.group(2)
        is_traitor = status == "Trators"

        # win settings, very safe way
        win_settings = row['=HYPERLINK("http://puu.sh/nJtmY/e2a5589f67.png","OS")'].split()
        if win_settings:
            win_sensitivity = to_float_or_none(win_settings[0].split("/")[0])
            if len(win_settings) >= 2:
                accl_setting = win_settings[1].strip().lower()
                if accl_setting.startswith("off"):
                    win_acceleration = False
                elif accl_setting.startswith("on"):
                    win_acceleration = True
            else:
                win_acceleration = None

        # osu settings, again it is focused on safe
        osu_multiplyer = to_float_or_none(
            row["Multiplier"].strip().split("~")[0].rstrip("xX"))
        if row["Raw"].strip().lower().startswith("on"):
            osu_raw = True
        elif row["Raw"].strip().lower().startswith("off"):
            osu_raw = False
        else:
            osu_raw = None

        # hardware setup info
        screen_resolution = row["Resolution"].strip().split("~")[0].split("x")
        if len(screen_resolution) >= 2:
            screen_width = to_float_or_none(screen_resolution[0])
            screen_height = to_float_or_none(screen_resolution[1])
        else:
            screen_width = None
            screen_height = None

        mousepad = strip_or_none(row["Mousepad"])
        keyboard = strip_or_none(row["Keyboard Model"])

        # mouse playstyle info
        playstyle = strip_or_none(row["Playstyle"])
        mouse = strip_or_none(row["Mouse Model"])
        dpi = to_float_or_none(row["DPI"].strip().rstrip("dpi"))
        polling = to_float_or_none(row["Polling"].lower().rstrip("hz"))

        # possibly calculate mouse area
        if win_sensitivity and osu_multiplyer and dpi and screen_width and screen_height:
            m = [0.00625, 0.0125, 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5]
            win_multi = m[int(win_sensitivity - 1)]

            # get play area
            if (screen_width / screen_height) >= (4 / 3):
                play_height = screen_height
                play_width = screen_height * (4 / 3)
            else:
                play_width = screen_width
                play_height = screen_width / (4 / 3)

            # get area
            effective_ppi = dpi * win_multi * osu_multiplyer
            area_width = round(25.4 * play_width / effective_ppi)
            area_height = round(25.4 * play_height / effective_ppi)
        else:
            area_width = None
            area_height = None

        # create new user
        users[userID] = {
            "name": userName,  # dont really need username tho
            "rank": None,
            "pp": None,
            "is_banned": False,
            "is_traitor": is_traitor,
            "windows_sensitivity": win_sensitivity,
            "windows_acceleration": win_acceleration,
            "osu_multiplyer": osu_multiplyer,
            "osu_raw": osu_raw,
            "screen_width": screen_width,
            "screen_height": screen_height,
            "playstyle": playstyle,
            "dpi": dpi,
            "polling": polling,
            "area_width": area_width,
            "area_height": area_height,
            "mouse": mouse,
            "mousepad": mousepad,
            "keyboard": keyboard,
        }

    # pprint.PrettyPrinter().pprint(users)
    write_json(users, "users.json")

if __name__ == "__main__":
    if len(sys.argv) == 2:
        main(sys.argv[1])
    else:
        print("Usage: python ./parse_tsv 'tsv/file/path'")


#Lista de Exercício 3 - Questão 38
#Dupla: 2020314273 - Cauã Alexandre e 2021327294 - Kallyne Ferro
#Disciplina: Programação Web
#Professor: Italo Arruda

#Um funcionário de uma empresa recebe aumento salarial anualmente: Sabe-se que:
#Esse funcionário foi contratado em 1995, com salário inicial de R$ 1.000,00;
#Em 1996 recebeu aumento de 1,5% sobre seu salário inicial;
#A partir de 1997 (inclusive), os aumentos salariais sempre correspondem ao dobro do percentual do ano anterior. Faça um programa que determine o salário atual desse funcionário. Após concluir isto, altere o programa permitindo que o usuário digite o salário inicial do funcionário.

class Salario:
    def __init__(self, salario_inicial):
        self.salario = salario_inicial

    def calcular_salario(self):
        try:
            aumento_1996 = self.salario * 0.015
            novo_salario = self.salario + aumento_1996

            percentual_aumento = 0.03
            ano = 1997

            while ano <= 2021:
                aumento = novo_salario * percentual_aumento
                novo_salario += aumento
                percentual_aumento *= 2
                ano += 1

            print(f"O salário atual do funcionário é R$ {novo_salario:.2f}")

        except:
            print("Error")

x = float(input("Digite o salário inicial do funcionário: "))

func1 = Salario(x)

func1.calcular_salario()


from tkinter import *
from tkinter import messagebox
from tkinter import ttk #css for tkinter
from configparser import ConfigParser

# import io
# import urllib.request
# import base64

import time

import ssl
ssl._create_default_https_context = ssl._create_unverified_context

import requests

weather_url = 'http://api.openweathermap.org/data/2.5/weather?q={},{}&appid={}'

bg_url = 'https://api.unsplash.com/search/photos?query={}&client_id={}'

config_file = 'config.ini'
config = ConfigParser()
config.read(config_file)
weather_api_key = config['weather_api_key']['key']
unsplash_access_key = config['unsplash_api_key']['access_key']

def get_image(city):
    image_index = 0
    result = requests.get(bg_url.format(city, unsplash_access_key))
    if result:
        json = result.json()
        first_url = json['results'][image_index]['urls']['raw']
        return first_url
        u = urllib.request.urlopen(first_url)
        image_byt = u.read()
        u.close()
        # photo = PhotoImage(data=base64.encodestring(image_byt))
        # return photo
    else:
        return None

def get_weather(city, country):
    result = requests.get(weather_url.format(city, country, weather_api_key))
    if result:
        json = result.json()
        # (City, Country, temp_celsius, temp_fahrenheit, icon, weather)
        city = json['name']
        country = json['sys']['country']
        temp_kelvin = json['main']['temp']
        temp_celsius = temp_kelvin - 273.15
        temp_fahrenheit = temp_celsius * 9/5 + 32
        icon = json['weather'][0]['icon']
        weather = json['weather'][0]['main']
        final = (city, country, temp_celsius, temp_fahrenheit, icon, weather)
        return final
    else:
        return None

def search():
    city = city_text.get()
    country = country_text.get()
    weather = get_weather(city, country)
    photo = get_image(city)
    if weather and city and country:
        location_lbl['text'] = '{}, {}'.format(weather[0], weather[1])
        image['bitmap'] = 'weather_icons/{}.png'.format(weather[4])
        weather_lbl['text'] = weather[5]
        temp_lbl['text'] = '{:.2f}°C \n {:.2f}°F'.format(weather[2], weather[3])
        url_lbl['text'] = photo

    elif not city or not country:
        messagebox.showerror('Error', 'Cannot find city: {} in country: {}'.format(city, country))
    else:
        messagebox.showerror('Error', 'Error Occured')

app = Tk()
app.title("Weather App")
app.geometry('900x700')

# city_image = Tk()

#Top Frame
top_frame = LabelFrame(app, text='Search', padx=50, pady=5)
top_frame.pack(side='top',padx=10, pady=10)

##Search Field
city_text = StringVar()
city_entry = ttk.Entry(top_frame, textvariable=city_text)
city_entry.pack(pady=2)

##Country Field
country_text = StringVar()
country_entry = ttk.Entry(top_frame, textvariable=country_text)
country_entry.pack(pady=2)

##Search Button
search_btn = ttk.Button(top_frame, text="Search by City, Country", width=20, command=search)
search_btn.pack(pady=10)

#Bottom Frame
bottom_frame = LabelFrame(app, text='Details', height=500, padx=100, pady=5)
bottom_frame.pack(side='top', padx=10, pady=10)

##Location
location_lbl = ttk.Label(bottom_frame, text='--', font=('bold', 20))
location_lbl.pack()

##Image
image = Label(bottom_frame, bitmap='--', relief='sunken')
image.pack(pady=10)

##Weather
weather_lbl = ttk.Label(bottom_frame, text='--')
weather_lbl.pack()

##Temperature
temp_lbl = ttk.Label(bottom_frame, text='--', font=('bold', 30))
temp_lbl.pack(padx=10, pady=10)

url_lbl = ttk.Label(bottom_frame, text='--')
url_lbl.pack(padx=10, pady=10)

#Bottom Frame
def bottom():
    statusbar = ttk.Label(app, text='Application Opened: {}'.format(time.asctime(time.localtime())), relief='sunken', anchor='w', font=('Italic', 15))
    statusbar.pack(side='bottom', fill='x')
bottom()

app.mainloop()


from __future__ import annotations
import logbook
from discord import Interaction
from discord import Message
from discord.ext.commands import Context
from json import dumps
from logging import Logger
from time import time
from utils import send_response
from yt_dlp.YoutubeDL import YoutubeDL

from yt_dlp import DownloadError

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from audio import Audio

log: Logger = logbook.getLogger("track")

class Track():
    def __init__(
            self,
            title: str,
            url: str,
            track_type: str,
            *,
            duration: int = 0,
            format: tuple[str, str] = ("", ""),
            guild_id: int = 0,
            url_original: str = "",
            video_id: str = ""
        ) -> None:
        self.title: str = title
        self.url: str = url
        self.track_type: str = track_type
        self.duration: int = duration
        self.format: tuple[str, str] = format
        self.guild_id: int = guild_id
        self.url_original: str = url_original
        self.video_id: str = video_id
        self.time_stamp: float = time()

    def __repr__(self) -> str:
        return dumps(self, default=vars, indent=4, ensure_ascii=False)

async def create_local_track(audio: Audio, cim: Context | Interaction | Message, url: str, track_type: str = "music") -> None | Track:
    if not cim.guild: return
    log.info(f"{cim.guild}: Creating local track from {url}")
    if "/.Cached Tracks/" in url:
        title: str = url[url.rfind("/") + 1 : -16]
    else:
        title: str = url[url.rfind("/") + 1:] if "/" in url else url
        title = title[:title.rfind(".")]
    return Track(title, url, track_type, guild_id=cim.guild.id)

async def create_stream_track(audio: Audio, cim: Context | Interaction | Message, url: str) -> None | Track:
    if not cim.guild: return
    log.info(f"{cim.guild}: Creating stream track from {url}")
    if url.startswith(("https://www.youtube.com/", "https://youtu.be/", "https://m.youtube.com/", "https://youtube.com/")):
        id: str | None = await _get_yt_video_id(url)
        if not id: return None
        url_local: str | None = audio.cached_tracks.get(id)
        if url_local:
            log.info(f"Found {id} in my cached tracks.")
            return await create_local_track(audio, cim, url_local, "music")

        log.info(f"{id} not found in cached tracks, downloading meta data...")
        url = f"https://www.youtube.com/watch?v={id}"   # Sanitize url

        video_info: dict | None = await _get_yt_video_info(audio, cim, url)
        if not video_info: return None
        protocol: str | None = video_info.get("protocol")
        if not protocol: return None
        log.info(f"Stream track protocol:\n{protocol}")

        if protocol == "m3u8_native":
            log.info(f"Stream track is a live stream.")
            title: str = video_info.get("title")[:-17]   # type: ignore
            url_original: str = url
            url_stream: str = video_info.get("url")      # type: ignore
            return Track(title, url_stream, "live", guild_id=cim.guild.id, url_original=url_original)
        else:
            log.info(f"Stream track is a normal stream.")
            duration: int = video_info["duration"] if video_info.get("duration") else await audio.get_audio_track_caching_duration_max()
            format: tuple[str, str] | None = await _get_yt_video_best_audio_format(video_info)
            if not format:
                await send_response(cim, "I could not find a suitable audio format to stream.")
                return None
            title: str = video_info["title"]
            url_original: str = url
            url_stream: str = format[1]
            track: Track = Track(title, url_stream, "stream", duration=duration, format=format, guild_id=cim.guild.id, url_original=url_original, video_id=id)
            await audio.download_q.put(track)
            return track
    elif url.startswith("https://open.spotify.com/"):
        await send_response(cim, "Spotify not implemented yet.")
        return None
    return None

async def create_meme_track(audio: Audio, cim: Context | Interaction | Message, url: str) -> None | Track:
    if url.lower() == "despacito":
        log.info(f"Creating meme track for \"{url}\"")
        return await create_stream_track(audio, cim, "https://www.youtube.com/watch?v=kJQP7kiw5Fk")
    return None

async def _get_yt_video_id(url: str) -> str | None:
    """Get the 11 chars long video id from a Youtube link."""
    if url.find("?v=") > 0:
        return url[url.find("?v=") + 3 : url.find("?v=") + 14]
    elif url.find("&v=") > 0:
        return url[url.find("&v=") + 3 : url.find("&v=") + 14]
    elif url.find(".be/") > 0:
        return url[url.find(".be/") + 4 : url.find(".be/") + 15]
    elif url.find("/shorts/") > 0:
        return url[url.find("/shorts/") + 8 : url.find("/shorts/") + 19]
    else:
        return None

async def _get_yt_video_info(audio: Audio, cim: Context | Interaction | Message, url) -> dict | None:
    options: dict[str, str | bool | int | Logger] = {
        "no_warnings": False,
        "default_search": "auto",
        "source_address": "0.0.0.0",
        "logger": logbook.getLogger("yt-dlp"),
        "age_limit": 21
    }
    try:
        video_info: dict | None = await audio.maon.loop.run_in_executor(
            None, lambda: YoutubeDL(options).extract_info(url, download=False)
        )
    except DownloadError as e:
        log.error(f"{cim.guild.name}: {e.__str__()[28:]}")  # type: ignore
        if "looks truncated" in e.__str__():
            await send_response(cim, "The link looks incomplete, paste it again, please.")
        elif "to confirm your age" in e.__str__():
            await send_response(cim, "The video is age gated and I couldn't proxy my way around it.")
        elif "HTTP Error 403" in e.__str__():
            await send_response(cim, "I received a `forbidden` error, I was locked out from downloading the meta data...\nYou could try again in a few seconds, though!")
        elif "Private video." in e.__str__():
            await send_response(cim, "The video has been privated and I can't view it.")
        else:
            await send_response(cim, "I could not download the video's meta data... maybe try again in a few seconds.")
        return None
    return video_info

async def _get_yt_video_best_audio_format(video_info: dict) -> tuple[str, str] | None:
    formats: dict[str, str] = {}
    for f in video_info.get("formats", [video_info]):
        formats[f.get("format_id")] = f.get("url")  # type: ignore

    log.info(f"Found formats: {formats.keys()}")
    if "251" in formats: return ("251", formats["251"])
    elif "140" in formats: return ("140", formats["140"])
    elif "250" in formats: return ("250", formats["250"])
    elif "249" in formats: return ("249", formats["249"])
    else: return None


import firebase_admin
from firebase_admin import credentials, firestore
import os
from gcloud import storage
from pprint import pprint
from datetime import datetime
import ast

from django import template

INDEX = 1
INDEX_historic = 1
INDEX_cv = 1
# Setup the connexion to the project
cred = credentials.Certificate("./website/serviceAccountKey.json")
#cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred)

db = firestore.client()

os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = r'./website/serviceAccountKey.json'
#os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = r'serviceAccountKey.json'

storage_client = storage.Client()

def increment_index_historic():
    """Fonction qui incrémente la variable globale INDEX_historic
        nécessaire pour l'unicité de notre historique dans la base de donnée"""

    global INDEX_historic
    INDEX_historic += 1
    return INDEX_historic

def increment_index():
    """Fonction qui incrémente la variable globale INDEX
        nécessaire pour l'unicité de notre fiche de poste dans la base de donnée"""
    global INDEX
    INDEX += 1
    return INDEX

def increment_index_cv():
    """Fonction qui incrémente la variable globale INDEX_historic
        nécessaire pour l'unicité de notre historique dans la base de donnée"""

    global INDEX_cv
    INDEX_cv += 1
    return INDEX_cv

def add_CV(name_hr,url_desc,file,name,date,lieu, dict_infos):
    global INDEX_historic
    #date = datetime.today().strftime('%Y-%m-%d')
    #name_file = "gs://snipehr_historic/"

    #dict_infos={"email":"norayda.nsiemo@gmail.com","phone":"+33768373205","skills":["Python","R","DevOps","MongoDB","documentation","flux","Python","R","Java","Jenkins","Airflow","Docker","Google","MySQL","MongoDB","Firebase","Tableau","documentation","flux"]}
    index_job =url_desc.split("/")[-1].split('.')[0]
    name_file = name_hr + "_" + index_job + "_" + str(INDEX_cv)
    increment_index_cv()

    #my_bucket = storage_client.get_bucket("snipehr_cvs")
    #pprint(vars(my_bucket))
    #blob = my_bucket.blob(name_file)
    #blob.upload_from_filename(file)
    #blob.upload_from_filename(file)

    db = firestore.client()
    new_cv={
        'name': name,
        'lieu' :lieu,
        'email': dict_infos['email'],
        'phone': dict_infos['phone'],
        'date' : date,
        'status': False,
        'url_resume': "gs://snipehr_cvs/" + name_file,
        'skills':dict_infos['skills']
    }
    #print(name_hr)
    db.collection('hrs').document(read_hr(name_hr).id).collection('resumes').add(new_cv)

def read_file_as_file(uri,name,date,poste):
    """Fonction qui lit la fiche de poste stocker dans le cloud storage à partir de l'URI """
    destination = "resumes/"+name+"_"+poste+"_"+date+'.pdf'
    blob=""
    if(uri != None):
        bucket = storage_client.get_bucket(uri.split("/")[-2])
        blob = bucket.blob(uri.split("/")[-1])
        blob = blob.download_to_filename(destination)
        #blob = blob.decode('utf-8')
    return blob

def read_file(uri):
    """Fonction qui lit la fiche de poste stocker dans le cloud storage à partir de l'URI """
    blob=""
    if(uri != None):
        bucket = storage_client.get_bucket(uri.split("/")[-2])

        blob = bucket.blob(uri.split("/")[-1])
        blob = blob.download_as_string()
        blob = blob.decode('utf-8')
    return blob

def liste_historic(name_hr,url_desc):
    """Fonction qui retourne la liste des historic pour une fiche de poste donnée"""
    liste =[]
    index_job =url_desc.split("/")[-1].split('.')[0]
    name_file = name_hr + "_" + index_job
    my_bucket = storage_client.get_bucket("snipehr_historic")

    blobs_all = list(my_bucket.list_blobs(prefix=name_file)) #récupère les fiches dont le nom du RH =name_hr et le job concerné à l'index du job en paramètre
    print(blobs_all)
    for blob in blobs_all:
        blob = blob.download_as_string()
        blob = blob.decode('utf-8')
        liste.append(ast.literal_eval(blob))
    return liste

def add_historic(name_hr, commentaire, url_desc):
    """Fonction qui ajoute un historique à une fiche de poste donnée"""
    global INDEX_historic
    date = datetime.today().strftime('%Y-%m-%d')
    #name_file = "gs://snipehr_historic/"
    index_job =url_desc.split("/")[-1].split('.')[0] #exemple dans l'URI gs://snipehr_job_desc/1.txt ça récupèrera 1
    my_bucket = storage_client.get_bucket("snipehr_historic")
    #pprint(vars(my_bucket))
    print(index_job)
    name = name_hr + "_" + index_job + "_" + str(INDEX_historic)+".txt"
    increment_index_historic()
    #text_file = open(name, "w")
    #n = text_file.write(commentaire)
    #text_file.close()

    historic =str({"date":date,"commentaire":commentaire})
    blob = my_bucket.blob(name)
    blob.upload_from_string(historic)

    print(blob)

def get_nb_missions_affectees(liste_jobs):
    """Fonction qui renvoie le nombre exacte de missions affecté à partir de la liste des jobs lié au profil"""
    nb=0
    for job in liste_jobs:
        if(job["status"]):
            nb+=1
    return nb

def set_status(name_hr,job_to_set):
    """Fonction qui met à jour le status d'une mission"""
    job =get_job(name_hr,job_to_set)
    print(job)
    print(job.to_dict()["status"])
    hr=read_hr(name_hr)
    status = job.to_dict()["status"]
    db.collection('hrs').document(hr.id).collection('job_description').document(job.id).update({"status": not status})

def read_company(name_hr):
    """Fonction qui retourne le nom de la compagnie du RH connecté"""
    hr=read_hr(name_hr)
    return hr.to_dict()["company"]

def chiffrement_message(message,clef):
    return None

def dechiffrement_message(message,clef):
    return None

def create_message(name_hr,message,nom,post):
    """Fonction qui crée et chiffre le message dans la base de donnée"""
    db = firestore.client()
    date = datetime.today().strftime('%Y-%m-%d')
    new_message={
        'date': f'{date}',
        'message': f'{message}',
        'candidat':f'{nom}',
        'post':f'{post}'
    }
    db.collection('hrs').document(read_hr(name_hr).id).collection('messages').add(new_message)

    return None

def create_job_desc(titre,lieu, date, competences, fiche, name_hr):
    global INDEX
    """Fonction qui ajoute une fiche de poste dans la base de donée"""
    url_desc="gs://snipehr_job_desc/"
    url_historic="gs://snipehr_historic/"
    lieu =lieu
    my_bucket = storage_client.get_bucket("snipehr_job_desc")
    #pprint(vars(my_bucket))
    name = str(INDEX)+".txt"
    increment_index()
    #text_file = open(name, "w")
    #n = text_file.write(fiche)
    #text_file.close()
    blob = my_bucket.blob(name)
    blob.upload_from_string(fiche)

    url_desc+=name
    db = firestore.client()
    new_job_desc={
        'titre': f'{titre}',
        'lieu' :f'{lieu}',
        'date': f'{date}',
        'status': False,
        'url_desc': f'{url_desc}',
        'url_historic': f'{url_historic}',
        'skills':f'{competences}'
    }
    #print(name_hr)
    db.collection('hrs').document(read_hr(name_hr).id).collection('job_description').add(new_job_desc)

def create_hr(name_hr, email_hr, mdp_hr, company_hr):
    """Fonction qui ajoute un RH à notre base de donée """
    db = firestore.client()
    # A voir pour ajouter le doc avec un id auto généré
    new_hr = {
        'name': f'{name_hr}',
        'email': f'{email_hr}',
        'mdp': f'{mdp_hr}',
        'company': f'{company_hr}'
    }

    db.collection('hrs').add(new_hr)
    db.collection('hrs').document(read_hr(name_hr).id).collections('job_description')

def set_hr(past_name,name_hr, email_hr, company_hr):
    db = firestore.client()
    new_hr = {
        'name': f'{name_hr}',
        'email': f'{email_hr}',
        'company': f'{company_hr}'
    }
    hr=read_hr(past_name)
    db.collection('hrs').document(hr.id).update(new_hr)

def get_job(name_hr,job_to_set):
    """Fonction qui nous permet d'avoir l'ID d'une jfiche de description"""
    col_jobs = db.collection('hrs').document(read_hr(name_hr).id).collection('job_description')
    jobs = col_jobs.stream()
    dictfilt = lambda x, y: dict([(i, x[i]) for i in x if i in set(y)])

    for job in jobs:
        print(dictfilt(job.to_dict(),("url_desc","date")))
        print(dictfilt(job_to_set,("url_desc","date")))
        #print(dictfilt(job.to_dict(),("date","titre","url_desc","url_historic")))
        if dictfilt(job_to_set,("url_desc","date")) == dictfilt(job.to_dict(),("url_desc","date")):
            return job

def read_hr(name_hr):
    """fonction qui nous perme d'avoir l'ID du RH à partir du nom"""
    # Only get 1 document or hrs
    col_hrs = db.collection('hrs').where("name", '==', f'{name_hr}')
    hrs = col_hrs.stream()

    for hr in hrs:
        #print(f'{hr.id} => {hr.to_dict()}')
        return hr

def read_hrs():
    """Fonction qui nous permet d'avoir la liste des RHs"""
    # Get the hole hrs collection
    col_hrs = db.collection('hrs')
    hrs = col_hrs.stream()
    for hr in hrs:
        print(f'{hr.id} => {hr.to_dict()}')

def test(email_hr, mdp_hr):
    hr = db.collection('hrs').where("email", '==', f'{email_hr}').where("mdp", '==', f'{mdp_hr}').get()
    print(hr)
    for h in hr:
        print(f'{h.id} => {h.to_dict()}')

def read_jobs(name_hr):
    """Fonction qui retourne la liste des fiches de poste associé à un RH"""
    collections = db.collection('hrs').document(read_hr(name_hr).id).collection("job_description").stream()
    list_jobs = []
    for collection in collections:
        list_jobs.append(collection.to_dict())
    return list_jobs

def read_resumes(name_hr):
    """Fonction qui retourne la liste des fiches de poste associé à un RH"""
    collections = db.collection('hrs').document(read_hr(name_hr).id).collection("resumes").stream()
    list_resumes = []
    for collection in collections:
        list_resumes.append(collection.to_dict())
    return list_resumes

def read_messages(name_hr):
    """Fonction qui retourne la liste des fiches de poste associé à un RH"""
    messages = db.collection('hrs').document(read_hr(name_hr).id).collection('messages').stream()
    list_messages = []
    i=1
    message_dict ={}
    for message in messages:
        message_dict=message.to_dict()
        message_dict['index']=i
        i+=1
        list_messages.append(message_dict)
    return list_messages

def get_job_title(name_hr,url_resume):
    titre =""
    url_desc = "gs://snipehr_job_desc/" + url_resume.split("/")[-1].split("_")[-2] +'.txt'
    jobs = read_jobs(name_hr)
    for job in jobs:
        if (job["url_desc"]==url_desc):
            titre=job["titre"]
    return titre

if __name__ == '__main__':
    #create_hr('Khalida', 'test@gmail.fr', 'azerty', 'ESGI')
    #read_hr('Test')
    #test('test@test.fr', 'test')
    #jobs = read_job(read_hr("Test"))
    #print(jobs)

    #titre=""
    #date=""
    #status= True
    #url_desc=""
    #url_historic =""
    #create_job_desc(titre, date, status, url_desc, url_historic, read_hr("Khalida"))
    name_hr ="Test"
    #job_to_set = {'date': '2022-08-25', 'titre': 'Data Analyst', 'url_desc': '', 'url_historic': ''}
    #print(get_job(name_hr,{'titre': 'Data', 'date': '2022-11-10', 'url_desc': 'gs://snipehr_job_desc/1.txt', 'url_historic': 'gs://snipehr_job_desc/'}).to_dict()["status"])
    #set_status(name_hr, job_to_set)
    #print(get_job(name_hr,job_to_set).to_dict()["status"])

    #print(read_job(read_hr("Test"))["jobs"])
    #set_status("Test",{'status': False, 'date': '2022-08-25', 'titre': 'Data Analyst', 'url_desc': '', 'url_historic': ''})

    #read_file("gs://snipehr_job_desc/1.txt")
    commentaire ="Test des commentaires"
    url_desc = "gs://snipehr_job_desc/1.txt"
    #add_historic(name_hr, commentaire, url_desc)
    #add_historic(name_hr, commentaire, url_desc)
    #print(liste_historic(name_hr, url_desc))

    #print(get_nb_missions_affectees(read_jobs(name_hr)))
    #add_CV(name_hr,url_desc,"CVNorayda_NSIEMO.pdf","Norayda NSIEMO","2022-09-23","Paris", dict())

    #read_file_as_file("gs://snipehr_cvs/Test_1_1","Norayda NSIEMO","2022-09-23","Data Engineer")


sayilar=(1,2,4,8,12,50,100)
harfler=("a","b","h","f","ğ","r")

sonuc=min(sayilar)
sonuc=max(sayilar)
"minimum ve maximum sonuçlarını söyler"

#ekleme
sayilar.append(20)
harfler.append("p")
sayilar.insert(3,11)
harfler.insert(4,"c")
"append sona ekler insert nereye koymak istersen"

#silme
sayilar.pop()
harfler.pop()
sayilar.remove(50)
"pop sondan silmeye başlar ama remove belirttiğini siler"

#sıralama
sayilar.sort()
harfler.sort()
sayilar.reverse()
"sort küçükten büyüğe reverse ise büyükten küçüğe sıralar"

print(sayilar.count(8)) #8'in kaç kere geçtiğine bakar
print(sayilar.index(12)) #12'nin kaçıncı indexte olduğunu söyler
sayilar.clear() #sayilar değişkeni içinde olan komutları siler

print(sonuc)


#!/usr/bin/env python
"""
Datapath for QEMU qdisk
"""

import urlparse
import os
import sys
import xapi
import xapi.storage.api.v5.datapath
import xapi.storage.api.v5.volume
import importlib
from xapi.storage.libs.libcow.datapath import QdiskDatapath
from xapi.storage import log

def get_sr_callbacks(dbg, uri):
    u = urlparse.urlparse(uri)
    sr = u.netloc
    sys.path.insert(
        0,
        '/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.' + sr)
    mod = importlib.import_module(sr)
    return mod.Callbacks()

class Implementation(xapi.storage.api.v5.datapath.Datapath_skeleton):
    """
    Datapath implementation
    """
    def activate(self, dbg, uri, domain):
        callbacks = get_sr_callbacks(dbg, uri)
        return QdiskDatapath.activate(dbg, uri, domain, callbacks)

    def attach(self, dbg, uri, domain):
        callbacks = get_sr_callbacks(dbg, uri)
        return QdiskDatapath.attach(dbg, uri, domain, callbacks)

    def deactivate(self, dbg, uri, domain):
        callbacks = get_sr_callbacks(dbg, uri)
        return QdiskDatapath.deactivate(dbg, uri, domain, callbacks)

    def detach(self, dbg, uri, domain):
        callbacks = get_sr_callbacks(dbg, uri)
        return QdiskDatapath.detach(dbg, uri, domain, callbacks)

    def open(self, dbg, uri, domain):
        callbacks = get_sr_callbacks(dbg, uri)
        return QdiskDatapath.epc_open(dbg, uri, domain, callbacks)

    def close(self, dbg, uri):
        callbacks = get_sr_callbacks(dbg, uri)
        return QdiskDatapath.epc_close(dbg, uri, callbacks)

if __name__ == "__main__":
    log.log_call_argv()
    CMD = xapi.storage.api.v5.datapath.Datapath_commandline(Implementation())
    CMD_BASE = os.path.basename(sys.argv[0])
    if CMD_BASE == "Datapath.activate":
        CMD.activate()
    elif CMD_BASE == "Datapath.attach":
        CMD.attach()
    elif CMD_BASE == "Datapath.close":
        CMD.close()
    elif CMD_BASE == "Datapath.deactivate":
        CMD.deactivate()
    elif CMD_BASE == "Datapath.detach":
        CMD.detach()
    elif CMD_BASE == "Datapath.open":
        CMD.open()
    else:
        raise xapi.storage.api.v5.datapath.Unimplemented(CMD_BASE)


import pymongo
import os
import pandas as pd
import json

def main():
    client = pymongo.MongoClient("mongodb://localhost:27017/")
    databases = client.list_database_names()

    if "fifa" not in databases:
        db = client["fifa"]

        players_collection = db["players"]
        ultimate_team_collection = db["ultimate_teams"]

        for file in os.listdir("data/players"):
            data = pd.read_csv("data/players/" + file)
            data["year"] = "20" + file.split(".")[0][-2:]
            if "female" in file:
                data["gender"] = "F"
            else:
                data["gender"] = "M"
            data_json = json.loads(data.to_json(orient='records'))

            for player in data_json:
                columns_to_format = ["ls", "st", "rs", "lw", "lf",	"cf", "rf", "rw", "lam", "cam",	"ram", "lm", "lcm",	"cm", "rcm", "rm", "lwb", "ldm", "cdm", "rdm", "rwb", "lb", "lcb", "cb", "rcb", "rb", "gk"]
                for column in columns_to_format:
                    if isinstance(player[column], str):
                        if "+" in player[column]:
                            split = player[column].split("+")
                            player[column] = int(split[0]) + int(split[1])
                        elif "-" in player[column]:
                            split = player[column].split("-")
                            player[column] = int(split[0]) - int(split[1])
                list_columns = ["player_positions", "player_tags", "player_traits"]
                for column in list_columns:
                    if player[column] is not None:
                        player[column] = [x.strip() for x in player[column].split(',')]
            players_collection.insert_many(data_json)
            print("Successfully loaded data for", file)

        print("Creating Indices for Faster Searching")
        players_collection.create_index([('year', pymongo.ASCENDING), ('gender', pymongo.ASCENDING)])
        players_collection.create_index([('year', pymongo.ASCENDING), ('gender', pymongo.ASCENDING), ('short_name', pymongo.ASCENDING)])
        players_collection.create_index([('year', pymongo.ASCENDING), ('gender', pymongo.ASCENDING), ('overall', pymongo.DESCENDING)])

        ultimate_team_collection.create_index([('year', pymongo.ASCENDING), ('username', pymongo.ASCENDING), ('team_name', pymongo.ASCENDING)])

    else:
        print("Data has been previously loaded.")

if __name__ == "__main__":
    main()


from types import SimpleNamespace
import random, string, sys

'''
To execute the testing function run the code with command line argument test
ex. ~$ python change_making.py test

For normal usage run the script with an amount as a command line argument
ex. ~$ python change_making.py 23.62
'''
class InvalidInputError(Exception):
    def __init__(self):
        super().__init__()

# Initialize denomination definitions (as integers less than 100 for precision)
DENOMINATIONS = {
        'quarter': 25,
        'dime': 10,
        'nickel': 5,
        'penny': 1
        }

def make_change(amount):
    '''
    Optimized Greedy Change Making Algorithm.
    '''
    # Initialize data structure.
    namespace = SimpleNamespace(
                is_valid = True,
                amount   = amount,
                dollars  = 0,
                cents    = 0,
                change   = SimpleNamespace(
                                dollar  = 0,
                                quarter = 0,
                                dime    = 0,
                                nickel  = 0,
                                penny   = 0)
                )
    # remove leading $ sign from input string, if it exists
    namespace = text_process(namespace)

    try:
        # Validate input.
        namespace = validate(namespace)
    except InvalidInputError:
        namespace.change = ('Amount must be a positive real number, '
                            'and in a valid format.')
    else:

        namespace.change.dollar = namespace.dollars

        # The Greedy Change Making Algorithm
        for denomination, value in DENOMINATIONS.items():
            while namespace.cents >= value:
                namespace.change.__dict__[denomination] += 1
                namespace.cents -= value
    finally:
        return namespace

def text_process(namespace):
    '''
    Removes any leading '$' from amount.
    '''
    try:
        # remove $ sign, if there is one
        index = namespace.amount.index('$')+1
        namespace.amount = namespace.amount[index:]
    except ValueError: # $ wasn't in the string
        pass
    finally:
        return namespace

def validate(namespace):
    '''
    The input validation function..

    rejects character strings, negative decimal values, decimals that have
    nonzero digits in place values exceeding hundreths, and empty strings
    '''
    try:
        assert float(namespace.amount) >= 0 # digital and nonnegative
        partition = namespace.amount.split('.')
        decimal_part = partition[1]
        # nonzero digits in place-values exceeding the hundredths place
        assert int(decimal_part) == 0 or len(decimal_part) <= 2
    except (ValueError, TypeError, AssertionError):
        namespace.is_valid = False
        raise InvalidInputError
    except IndexError: # input was not floating-point
        namespace.dollars = int(namespace.amount)
    else: # input was of the form x.yz
        namespace.dollars, namespace.cents = int(partition[0]), int(decimal_part)
    return namespace

def display(namespace):

    if namespace.is_valid:

        length = len(str(namespace.change.dollar))
        fill = ' '*length
        namespace.amount = float(namespace.amount)
        print('''
 input :   ${namespace.amount:.2f}
 output:

    {namespace.change.dollar}  dollars
    {namespace.change.quarter}{fill} quarters
    {namespace.change.dime}{fill} dimes
    {namespace.change.nickel}{fill} nickels
    {namespace.change.penny}{fill} pennies
'''.format_map(vars()))

    else:
        print('''
 input :   {namespace.amount}
 output:   {namespace.change}'''.format_map(vars()))

def test():
    '''
    Test cases are developed and executed here.
     - test_1 : charater input > error
     - test_2 : negative real number input > error
     - test_3 : the empty string > error
     - test_4 : nonzero digits in place values exceeding hundredths > error
     - test_5 : the integer zero > a multiset that is the empty set
     - test_6 : a pseudorandom nonnegative real number less than or equal to one
                million. > a populated multiset
    '''
    test_1 = random.choice(string.ascii_letters + string.punctuation)
    test_2 = -1 * (random.randint(0, 1000000) + round(random.random(), 2))
    test_3 = ''
    test_4 = random.randint(0, 1000000) + random.random()
    test_5 = 0
    test_6 = random.randint(0, 1000000) + round(random.random(), 2)

    # normalize test case data types
    cases = list(map(str, [test_1, test_2, test_3, test_4, test_5, test_6]))

    # add set membership information for each case
    sets = ['non-numeric character', 'negative real number', 'empty string',
            'nonzero digits in place-values exceeding hundredths',
            'zero integer',
            'a correct input value']
    cases = list(zip(cases, sets))

    # process test cases
    for index, case in enumerate(cases):
        amount, set_membership = case[0], case[1]
        test_number = index +1
        # execute the algorithm
        namespace   = make_change(amount)
        print('\n[Test Case {test_number} ({set_membership})]:'.format_map(
                vars()))
        display(namespace)

def main():
    try:
        amount = sys.argv[1]
    except IndexError:
        print('Must enter an amount.')
    else:
        if amount == 'test':
            test()
        else:
            namespace = make_change(amount)
            display(namespace)

if __name__ == '__main__':
    main()


# coding: utf-8
class Queue:

    def __init__(self):
        self.tree_list = []

    def enqueue(self, tree):
        self.tree_list.append(tree)

    def dequeue(self):
        return self.tree_list.pop(0)

    def is_empty(self):
        pass

if __name__ == '__main__':
    tree_list = []
    queue = Queue()

    n = int(input())

    for i in range(1, n):
        f, s = map(int, input().split())
        queue.enqueue([f, s])

    c = 0
    while True:
        tree = queue.dequeue()
        next_tree = queue.dequeue()

        if tree[0] in next_tree:
            k = next_tree.index(tree[0])
            queue.enqueue(sorted(next_tree[k-1:], reverse=True) + tree)
            # if k != 0 and k != len(tree) and queue.tree_list not in tree:
            #     queue.enqueue(tree)
            print(queue.tree_list)
            continue

        if tree[1] in next_tree:
            k = next_tree.index(tree[1])
            queue.enqueue(sorted(tree, reverse=True) + next_tree[:k])
            # if k != 0 and k != len(tree) and queue.tree_list not in tree:
            #     queue.enqueue(tree)
            print(queue.tree_list)
            continue

        queue.enqueue(tree)
        queue.enqueue(next_tree)

        if c > 10:
            exit()

        c += 1


lines="hi hello hi hello"
# o/p
#hai,2
#hello,2

#split function
words=lines.split(" ")
print(words)

dic={}
for word in words:#hai hello
    if(word not in dic):#hai not in dic,hello not in dic
        dic[word]=1
    else:
        dic[word]+=1
print(dic)


import re
from General.utilities import *
from General import feedparser

def getContentExtend(RssUrl,Pattern, FetchNumber=None):
  #get the feed
  FeedContent = cachedFetch(RssUrl)
  feed = feedparser.parse(FeedContent)
  FetchList=[]
  Num=0
  reObj=re.compile(Pattern,re.M|re.S|re.U)
  reObj_noScriptAll = re.compile("<script.*?<\/script>", re.M|re.S|re.U|re.I)
  for entry in feed.entries:
    FetchList.append(entry["link"])
    Num = Num +1
    if (not FetchNumber is None) and FetchNumber == Num:
      break;
  FetchResult = MultiFetch(FetchList)
  for entry in feed.entries:
    if FetchResult.has_key(entry["link"]):
      htmlContent=ConvertToUnicode(FetchResult[entry["link"]])
      noScriptAll = reObj_noScriptAll.split(htmlContent)
      htmlContent = " ".join(noScriptAll)
      reResult = reObj.search(htmlContent)
      if reResult:
        if len(reResult.groups()) != 0:
          entry["htmlContent"]= reResult.group(1)
        else:
          entry["htmlContent"]= reResult.group(0)

  return feed

# vim:tabstop=2 expandtab shiftwidth=2


# -*- coding: utf-8 -*-
"""
Created on Fri Sep 28 15:42:03 2018

@author: tf
"""

from numpy import *
import operator
import os, sys

#2.1 a simple kNN classifier
def classify0(inX, dataSet, labels, k):
    '''
    a simple kNN classifier
    '''
    dataSetSize = dataSet.shape[0];
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5
    sortedDistIndices = distances.argsort()
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndices[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

#2.2 Helen dating
def file2matrix(filename):
    '''
    convert a txt file to matrix
    '''
    fr = open(filename)
    lineArray = fr.readlines()
    lineNum = len(lineArray)
    retMat = zeros((lineNum, 3))
    classLabelVector = []
    idx = 0
    for line in lineArray:
        line = line.strip()
        listFromLine = line.split('\t')
        retMat[idx, :] = listFromLine[0:3]
        if listFromLine[-1] == 'largeDoses':
            classLabelVector.append(1)
        elif listFromLine[-1] == 'smallDoses':
            classLabelVector.append(2)
        elif listFromLine[-1] == 'didntLike':
            classLabelVector.append(3)
        else:
            classLabelVector.append(0)
        idx += 1
    return retMat, classLabelVector

def autoNorm(dataSet):
    '''
    data normolization
    normval = (oldval - min) / (max - min)
    '''
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals, (m, 1))
    normDataSet = normDataSet / tile(ranges, (m, 1))
    return normDataSet, ranges, minVals

def datingClassTest():
    '''
    test kNN classifier on dating data
    '''
    hoRatio = 0.1
    datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m = normMat.shape[0]
    numTestVecs = int(m * hoRatio)
    errorCnt = 0
    for i in range(numTestVecs):
        classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 4)
        print('the classifier came back with: %d, the real answer is: %d' %(classifierResult, datingLabels[i]))
        if classifierResult != datingLabels[i]:
            errorCnt += 1
    errorRate = errorCnt / numTestVecs
    print('the total error rate is: %f' %(errorRate))

#2.3 handwriting recognizing
def img2vector(filename):
    '''
    convert a 32*32-size image file to a 1*1024-size vector
    '''
    retVect = zeros((1,1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            retVect[0, 32*i+j] = int(lineStr[j])
    return retVect

def handwritingClassTest():
    '''
    test kNN classifier on handwriting data
    '''
    hwLabels = []
    trainingFileList = os.listdir('trainingDigits')
    mTrain = len(trainingFileList)
    trainingMat = zeros((mTrain, 1024))
    for i in range(mTrain):
        filename = trainingFileList[i]
        classNum = int(filename.split('.')[0].split('_')[0])
        hwLabels.append(classNum)
        trainingMat[i, :] = img2vector('trainingDigits/%s' % filename)
    testFileList = os.listdir('testDigits')
    errorCnt = 0
    mTest = len(testFileList)
    for i in range(mTest):
        filename = testFileList[i]
        classNum = int(filename.split('.')[0].split('_')[0])
        vecUnderTest = img2vector('trainingDigits/%s' % filename)
        classifierResult = classify0(vecUnderTest, trainingMat, hwLabels, 3)
        print('the classifier came back with: %d, the real answer is: %d' %(classifierResult, classNum))
        if classifierResult != classNum:
            errorCnt += 1
    errorRate = errorCnt / mTest
    print('\nthe total number of error rate is: %f' % errorRate)


import eventlet
import msgpack
import random
from copy import copy
from datetime import datetime

from . import cmds
from . import msgs

import os
log_file = open(os.path.join(os.getcwd(), 'client.log'), 'w')

def write_log(msg):
    global log_file
    log_file.write(
        "{0} - {1}\n".format(datetime.now(), str(msg)),
    )

class RaftClient(object):

    def __init__(self, server_address_list):
        self.server_address_list = server_address_list
        self._leader_address = None
        self._leader_sock = None
        self.status = 'init'
        self.cmd_seq = 0
        self.client_id = None

    @classmethod
    def select_server(cls, server_address_list):
        return random.choice(server_address_list)

    def register(self):
        cmd = cmds.get_client_register_cmd()
        cmd_msg = msgs.get_client_register_req_msg(cmd)
        self.cmd_seq = 0
        ret = self.execute_command(cmd_msg)
        self.client_id = ret['resp'][1]
        return ret

    def get_next_seq(self):
        self.cmd_seq += 1
        return self.cmd_seq

    def send_command_req(self, command_msg):
        self._leader_sock.sendall(command_msg)

    def set_value(self, key, value):
        cmd = cmds.get_client_update_cmd(
            key,
            value,
        )
        cmd_msg = msgs.get_client_update_req_msg(
            self.client_id,
            self.get_next_seq(),
            cmd,
        )
        return self.execute_command(cmd_msg)

    def get_value(self, key):
        cmd = cmds.get_client_query_cmd(key)
        cmd_msg = msgs.get_client_query_req_msg(
            self.client_id,
            self.get_next_seq(),
            cmd,
        )
        return self.execute_command(cmd_msg)

    def wait_command_ret(self):
        unpacker = msgpack.Unpacker()
        while True:
            chunk = self._leader_sock.recv(1024)
            if len(chunk) == 0:
                break
            unpacker.feed(chunk)
            try:
                return unpacker.next()
            except StopIteration:
                pass
        return None

    def execute_command(self, command_msg):
        s_addr_list = copy(self.server_address_list)
        while len(s_addr_list) > 0:
            try:
                if self._leader_address is None:
                    self._leader_address = RaftClient.select_server(s_addr_list)
                    write_log(
                        "selected server {0}".format(self._leader_address))
                if self._leader_sock is None:
                    self._leader_sock = eventlet.connect(self._leader_address)
                timeout = eventlet.Timeout(2)
                try:
                    self.send_command_req(command_msg)
                    write_log(
                        "sent {0} - cmd: {1}".format(
                            self._leader_address,
                            msgpack.unpackb(command_msg),
                        )
                    )
                    ret = self.wait_command_ret()
                finally:
                    timeout.cancel()
                if ret is not None:
                    if ret['success']:
                        if ret['resp'][2] < self.cmd_seq:
                            continue
                        return ret
                    else:
                        if 'leader_hint' in ret:
                            self._leader_sock.close()
                            self._leader_sock = None
                            self._leader_address = (
                                ret['leader_hint'][0],
                                ret['leader_hint'][1] + 1000,
                            )
                            continue
            except (eventlet.timeout.Timeout, Exception) as e:
                write_log("hit exception:\n {0}".format(str(e)))
                pass
            if self._leader_address in s_addr_list:
                s_addr_list.remove(self._leader_address)
            if len(s_addr_list) == 0:
                s_addr_list = copy(self.server_address_list)
            self._leader_address = None
            self._leader_sock = None


#
# @lc app=leetcode id=202 lang=python3
#
# [202] Happy Number
#

# @lc code=start
class Solution:
    def isHappy(self, n: int) -> bool:
        seen = set()
        while n != 1:
            res = 0
            for digit in str(n):
                res += int(digit) ** 2
            n = res
            if n in seen: return False
            else: seen.add(n)
        return True

# @lc code=end


import sys

input = sys.stdin.readline

def get_primenumber_under(n):
    is_primes = [False, False] + [True for _ in range(2, n+1)]
    for i in range(2, int(n**0.5)+1):
        j = 2
        while i*j <= n:
            if is_primes[i*j]:
                is_primes[i*j] = False
            j += 1
    return is_primes

def is_prime(n):
    if n > len(primenumbers):
        for num, primeFlag in enumerate(primenumbers):
            if primeFlag:
                if n % num == 0:
                    return False
            if num > n//2:        # 정렬된 소수 배열
                break
    else:
        if not primenumbers[n]:
            return False
    return True

T = int(input())
# 골드바흐의 추측 : 4보다 큰 짝수는 두 소수의 합으로 나타낼 수 있다.
# 2 -> x
# 3 -> x
# 짝수 -> o
# 홀수 -> 두 수의 합이 홀수가 되는 경우는 짝수 + 홀수 -> 소수 중 짝수는 2밖에 없다.
# 따라서 홀수의 경우 해당 수에서 2를 뺀 값이 소수인지를 확인하면 된다.
#   -> 해당 수를 sqrt(n) 내의 소수들로 나눠서 나눠지면 소수가 아닌 것으로 판별할 수 있다.
primenumbers = get_primenumber_under(2*(10**6))

for t in range(T):
    A, B = map(int, input().rstrip().split())
    sum_AB = A+B
    if sum_AB == 2 or sum_AB == 3:
        print('NO')
        continue
    elif sum_AB % 2 == 0:
        print('YES')
        continue
    else:
        print('YES') if is_prime(sum_AB-2) else print('NO')


# Take the code from the How To Decode A Website exercise
# (if you didn’t do it or just want to play with some different code, use the code from the solution),
#  and instead of printing the results to a screen, write the results to a txt file.
#  In your code, just make up a name for the file you are saving to.

# Extras:

# Ask the user to specify the name of the output file that will be saved.

import requests
from bs4 import BeautifulSoup

base_url = 'http://www.nytimes.com'
r = requests.get(base_url)
soup = BeautifulSoup(r.text)
titles = []
for story_heading in soup.find_all(class_="story-heading"):
    if story_heading.a:
        # print(story_heading.a.text.replace("\n", " ").strip())
        titles.append(story_heading.a.text.replace("\n", " ").strip())
    else:
        print(story_heading.contents[0].strip())

name_file = input('Rename the file please ')

with open('{}.txt'.format(name_file), 'w') as open_file:
  open_file.write(str(titles))


Test_case = int(input())

for t in range(Test_case):
    N = int(input())
    count = [1] * N
    idx = 0
    carrot = list(map(int, input().split()))
    for i in range(1, N):
        if carrot[i] > carrot[i-1]:
            count[idx] += 1
        else:
            idx += 1
    max_count = 0
    for j in count:
        if j > max_count:
            max_count = j
        else:
            continue

    print(f'#{t+1} {max_count}')


"""
    USC Spring 2020
    INF 553 Foundations of Data Mining
    Assignment 3

    Student Name: Jiabin Wang
    Student ID: 4778-4151-95
"""
from pyspark import SparkConf, SparkContext, StorageLevel
from trainAuxiliary import *
'''
import os
import re
import json
import time
import sys
import math
import random
import itertools
'''

if __name__ == "__main__":
    time_start = time.time()

    # Get the input parameters
    input_file_path = sys.argv[1] #"./Dataset/train_review.json"
    model_file_path = sys.argv[2]
    cf_type = sys.argv[3]

    # Configure the Spark
    conf = (
        SparkConf()
        .setAppName("task3")
        .set("spark.driver.memory", "4g")
        .set("spark.executor.memory", "4g")
    )
    sc = SparkContext(conf=conf)
    sc.setLogLevel("ERROR")

    if cf_type == "item_based":
        pairCollection = itemBasedTrain(sc, input_file_path, model_file_path)
        itemBasedOutputModel(model_file_path, pairCollection)

    if cf_type == "user_based":
        result = userBasedTrain(sc, input_file_path, model_file_path)
        userBasedOutputModel(model_file_path, result)

    time_end = time.time()
    print("Duration: ", time_end - time_start, "s")


# 死锁：一直等待对方释放锁的情景叫做死锁

# 需求：多线程同时根据下标在列表中取值，要保证同一时刻只能有一个线程去取值

import threading

lock = threading.Lock()

def get_value(index):
    # 上锁
    lock.acquire()
    my_list = [1, 4, 6]
    if index >= len(my_list):
        print('下标越界')
        return
        # 取值不成功， 也要释放互斥锁， 不要影响后面进程进行
        lock.release()
    else:
        print(f'value = {my_list[index]}')
    lock.release()

if __name__ == '__main__':
    for i in range(10):
        value_thread = threading.Thread(target=get_value, args=(i,))
        value_thread.start()


from tkinter import *
from piece import piece
window = Tk()
labels=[0]*240
for r in range(24):
    for c in range(10):
        labels[r*10+c]=Label(window, bg='black', height=1, width=2)
        labels[r *10+c].grid(row=r,column=c, sticky=S+N+E+W)

firstSquare=piece(window, labels, 0, 10,5)
window.mainloop()


from django.conf.urls.defaults import *
from django.contrib import admin
import os.path
admin.autodiscover()
MEDIA_ROOT = os.path.join(os.path.abspath(os.path.dirname(__file__)), "media")

urlpatterns = patterns('',
    (r'^admin/doc/', include('django.contrib.admindocs.urls')),
    (r'^admin/', include(admin.site.urls)),

    (r'^media/(?P<path>.*)$', 'django.views.static.serve',
     {'document_root': MEDIA_ROOT}),
    url(r'^p/(\w+)$', 'air.views.object', name='object'),
    url(r'^compare/(\w+)$', 'air.views.compareTo', name='compareTo'),
    url(r'^compare/(\w+)/(\w+)$', 'air.views.compare', name='compare'),
    url(r'^categories/$', 'air.views.categories', name='categories'),
    url(r'^category/$', 'air.views.category', name='category'),
    url(r'^projection/(\w+)/(\w+)$', 'air.views.projection', name='projection'),
    url(r'^add/$', 'air.views.add', name='add'),
    url(r'^reset/$', 'air.views.reset', name='reset'),
    url(r'^$', 'air.views.explore', name='explore'),
)


"""Tool for processing pytd files.

pytd is a type declaration language for Python. Each .py file can have an
accompanying .pytd file that specifies classes, argument types, return types
and exceptions.
This binary processes pytd files, typically to optimize them.

Usage:
  pytd_tool [flags] <inputfile> <outputfile>
"""

import argparse
import sys

from pytype import utils
from pytype.imports import builtin_stubs
from pytype.pyi import parser
from pytype.pytd import optimize
from pytype.pytd import pytd_utils

def make_parser():
  """Use argparse to make a parser for command line options."""

  o = argparse.ArgumentParser(
      usage="%(prog)s [options] infile.pytd [outfile.pytd]")

  # Input and output filenames
  o.add_argument("input", help="File to process")
  o.add_argument("output", nargs="?",
                 help=("Output file (or - for stdout). If output is omitted, "
                       "the input file will be checked for errors."))

  o.add_argument(
      "-O", "--optimize", action="store_true",
      dest="optimize", default=False,
      help="Optimize pytd file.")
  o.add_argument(
      "--lossy", action="store_true",
      dest="lossy", default=False,
      help="Allow lossy optimizations, such as merging classes.")
  o.add_argument(
      "--max-union", type=int, action="store",
      dest="max_union", default=4,
      help="Maximum number of objects in an 'or' clause.\nUse with --lossy.")
  o.add_argument(
      "--use-abcs", action="store_true",
      dest="use_abcs", default=False,
      help="Inject abstract bases classes for type merging.\nUse with --lossy.")
  o.add_argument(
      "--remove-mutable", action="store_true",
      dest="remove_mutable", default=False,
      help="Remove mutable parameters.")
  o.add_argument(
      "-V", "--python_version", type=str, action="store",
      dest="python_version", default=None,
      help=("Python version to target (\"major.minor\", e.g. \"3.10\")"))
  o.add_argument(
      "--multiline-args", action="store_true",
      dest="multiline_args", default=False,
      help="Print function arguments one to a line.")
  return o

def main():
  argument_parser = make_parser()
  opts = argument_parser.parse_args()
  if opts.python_version:
    python_version = utils.version_from_string(opts.python_version)
  else:
    python_version = sys.version_info[:2]
  try:
    utils.validate_version(python_version)
  except utils.UsageError as e:
    sys.stderr.write(f"Usage error: {e}\n")
    sys.exit(1)

  options = parser.PyiOptions(python_version=python_version)

  with open(opts.input) as fi:
    sourcecode = fi.read()
    try:
      parsed = parser.parse_string(
          sourcecode, filename=opts.input, options=options)
    except parser.ParseError as e:
      sys.stderr.write(str(e))
      sys.exit(1)

  if opts.optimize:
    parsed = optimize.Optimize(
        parsed,
        pytd_utils.Concat(*builtin_stubs.GetBuiltinsAndTyping(options)),
        lossy=opts.lossy,
        use_abcs=opts.use_abcs,
        max_union=opts.max_union,
        remove_mutable=opts.remove_mutable,
        can_do_lookup=False)

  if opts.output is not None:
    out_text = pytd_utils.Print(parsed, opts.multiline_args)
    if opts.output == "-":
      sys.stdout.write(out_text)
    else:
      with open(opts.output, "w") as out:
        out.write(out_text)

if __name__ == "__main__":
  main()


"""Point-wise Spatial Attention Network"""
import torch
import torch.nn as nn

up_kwargs = {'mode': 'bilinear', 'align_corners': True}
norm_layer = nn.BatchNorm2d

class _ConvBNReLU(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0,
                 dilation=1, groups=1, relu6=False, norm_layer=norm_layer):
        super(_ConvBNReLU, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, dilation, groups, bias=False)
        self.bn = norm_layer(out_channels)
        self.relu = nn.ReLU6(True) if relu6 else nn.ReLU(True)

    def forward(self, x):
        x = self.conv(x)
        x = self.bn(x)
        x = self.relu(x)
        return x

class PSAHead(nn.Module):
    def __init__(self, in_channels=768, num_classes=6, norm_layer=norm_layer, in_index=3):
        super(PSAHead, self).__init__()
        self.in_index = in_index
        # psa_out_channels = crop_size // stride_rate ** 2
        psa_out_channels = (512 // 32) ** 2
        self.psa = _PointwiseSpatialAttention(in_channels, psa_out_channels, norm_layer)

        self.conv_post = _ConvBNReLU(psa_out_channels, in_channels, 1, norm_layer=norm_layer)
        self.project = nn.Sequential(
            _ConvBNReLU(in_channels * 2, in_channels // 2, 3, padding=1, norm_layer=norm_layer),
            nn.Dropout2d(0.1, False),
            nn.Conv2d(in_channels // 2, num_classes, 1))

    def _transform_inputs(self, inputs):
        if isinstance(self.in_index, (list, tuple)):
            inputs = [inputs[i] for i in self.in_index]
        elif isinstance(self.in_index, int):
            inputs = inputs[self.in_index]
        return inputs

    def forward(self, inputs):
        x = self._transform_inputs(inputs)
        global_feature = self.psa(x)
        out = self.conv_post(global_feature)
        out = torch.cat([x, out], dim=1)
        out = self.project(out)

        return out

class _PointwiseSpatialAttention(nn.Module):
    def __init__(self, in_channels, out_channels, norm_layer=nn.BatchNorm2d):
        super(_PointwiseSpatialAttention, self).__init__()
        reduced_channels = out_channels // 2
        self.collect_attention = _AttentionGeneration(in_channels, reduced_channels, out_channels, norm_layer)
        self.distribute_attention = _AttentionGeneration(in_channels, reduced_channels, out_channels, norm_layer)

    def forward(self, x):
        collect_fm = self.collect_attention(x)
        distribute_fm = self.distribute_attention(x)
        psa_fm = torch.cat([collect_fm, distribute_fm], dim=1)
        return psa_fm

class _AttentionGeneration(nn.Module):
    def __init__(self, in_channels, reduced_channels, out_channels, norm_layer):
        super(_AttentionGeneration, self).__init__()
        self.conv_reduce = _ConvBNReLU(in_channels, reduced_channels, 1, norm_layer=norm_layer)
        self.attention = nn.Sequential(
            _ConvBNReLU(reduced_channels, reduced_channels, 1, norm_layer=norm_layer),
            nn.Conv2d(reduced_channels, out_channels, 1, bias=False))

        self.reduced_channels = reduced_channels

    def forward(self, x):
        reduce_x = self.conv_reduce(x)
        attention = self.attention(reduce_x)
        n, c, h, w = attention.size()
        attention = attention.view(n, c, -1)
        reduce_x = reduce_x.view(n, self.reduced_channels, -1)
        fm = torch.bmm(reduce_x, torch.softmax(attention, dim=1))
        fm = fm.view(n, self.reduced_channels, h, w)

        return fm


# Python
class Solution(object):
    def createTargetArray(self, nums, index):
        """
        :type nums: List[int]
        :type index: List[int]
        :rtype: List[int]
        """
        arrList = list()

        for x in range(0, len(index)):
            arrList.insert(index[x], nums[x])
        return arrList


from tinydb import TinyDB, Query, where

db = TinyDB("data.json", indent=4)

db.update({"score": 10}, where ("name") == "Patrick")
db.update({"roles": ["Junior"]})
db.update({"roles": ["Expert"]}, where("name") == "Patrick")

db.upsert({"name": "Pierre", "score": 120, "roles": ["Senior"]}, where("name") == "Pierre")

db.remove(where("score") == 0)

# db.truncate() # supprime tout le contenu de la db


#from IPython.display
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
from IPython.display import display
from IPython.core.display import HTML
import json
from pprint import pprint
import os
import time
#import md5
import hashlib
import os

from aliyunsdkcore.profile import region_provider
from aliyunsdkcore.client import AcsClient
import base64
import aliyunsdkimagesearch.request.v20190325.AddImageRequest as AddImageRequest
import aliyunsdkimagesearch.request.v20190325.DeleteImageRequest as DeleteImageRequest
import aliyunsdkimagesearch.request.v20190325.SearchImageRequest as SearchImageRequest

import os
import time
from pprint import pprint

def list_images(image_folder):
    images = {}
    for file in os.listdir(image_folder):
        if file.endswith(".jpg") or file.endswith(".png"):
            image_path = os.path.join(image_folder, file)
            # print(os.path.abspath(image_path))
            images[image_path] = file
    return images

def get_Piccontent_from_file(image_path):
    file_object = open(image_path)
    file_content = None
    try:
        file_content1 = file_object.read()
        import base64
        file_content = base64.b64encode(file_content1) # ('data to be encoded')
        # data = base64.b64decode(encoded)
    finally:
        file_object.close()
    return file_content

def my_image_preview(image_path, box, cate, color="red"):
    #img1 = Image(filename = image_path, width=100, height=100)
    img1 = Image.open(image_path)
    if box is not None and box != '':
        draw = ImageDraw.Draw(img1)
        bb = box.split(",")
        x0 = float(bb[0])
        y0 = float(bb[2])
        x1 = float(bb[1])
        x2 = float(bb[3])
        draw.rectangle([(x0, y0), (x1, x2)], outline=color)
        if cate is not None and cate != "":
            draw.text((x0, y0), cate, fill=color)
    img = img1
    return img

###########################################################
###########################################################
def match_cate_desc(cate_id):
    AllCategories = [{'Id': 0, 'Name': 'Tops'}, {'Id': 1, 'Name': 'Dress'},{'Id': 2, 'Name': 'Bottoms'},{'Id': 3, 'Name': 'Bag'}, {'Id': 4, 'Name': 'Shoes'},{'Id': 5, 'Name': 'Accessories'},{'Id': 6, 'Name': 'Snack'},{'Id': 7, 'Name': 'Makeup'},{'Id': 8, 'Name': 'Bottle'},{'Id': 9, 'Name': 'Furniture'},{'Id': 20, 'Name': 'Toy'},{'Id': 21, 'Name': 'Underwear'},{'Id': 22, 'Name': 'Digital device'},{'Id': 88888888, 'Name': 'Other'}]
    for c in AllCategories:
        if cate_id == c['Id']:
            return c['Name']
    return 'Other'

def my_image_upload_base(requestClient, endpoint, instanceName, ProductId, image_name, image_path, cate_id, cate_desc, obj_region):
    # load file
    request = AddImageRequest.AddImageRequest()
    request.set_endpoint(endpoint)
    request.set_InstanceName(instanceName)
    image_content = {'name': image_name, 'path': image_path, 'cate_id':cate_id, 'cate_desc':cate_desc, 'obj_region':obj_region}
    request.set_CustomContent(json.dumps(image_content))
    request.set_ProductId(ProductId)
    request.set_PicName(image_name)
    #if cate_id is not None:
    request.set_CategoryId(cate_id)
    print("=======", cate_id, image_name)
    with open(image_path, 'rb') as imgfile:
        encoded_pic_content = base64.b64encode(imgfile.read())
        request.set_PicContent(encoded_pic_content)
    response = requestClient.do_action_with_exception(request)
    r = json.loads(response)
    # print(response)
    return r

#def my_image_upload_for_category():

def my_image_upload_for_similarity_search(requestClient, endpoint, instanceName, ProductId, image_name, image_path, cate_id, cate_desc, obj_region):
    r = my_image_upload_base(requestClient, endpoint, instanceName, ProductId, image_name, image_path, cate_id, cate_desc, obj_region)
    #print("== image upload return result ==")
    #pprint(r)
    cate_desc = match_cate_desc(r['PicInfo']['CategoryId'])
    r['cate_desc'] = cate_desc
    r['cate_id'] = r['PicInfo']['CategoryId']
    r['obj_region'] = r['PicInfo']['Region']
    #pprint(r)
    #display(my_image_preview(image_path, r['obj_region'], r['cate_desc']))
    #print(image_path, ' | found category_desc: ', r['cate_desc'], r['cate_id'], ' | found category_id: ', r['cate_id'], ' | found region: ',  r['obj_region'])
    return r

###########################################################
###########################################################
def my_image_search_base(requestClient, instanceName, image_path):
    request = SearchImageRequest.SearchImageRequest()
    request.set_InstanceName(instanceName)
    with open(image_path, 'rb') as imgfile:
        encoded_pic_content = base64.b64encode(imgfile.read())
        request.set_PicContent(encoded_pic_content)
    response = requestClient.do_action_with_exception(request)
    r = json.loads(response)
    #pprint(r)
    return r

def my_image_search_for_category_detection(requestClient, instanceName, image_path):
    r = my_image_search_base(requestClient, instanceName, image_path)
    #r = json.loads(r)
    #pprint(r)
    category_desc = ''
    for c in r['PicInfo']['AllCategories']:
        if r['PicInfo']['CategoryId'] == c['Id']:
            category_desc = c['Name']
    r['cate_desc'] = category_desc
    r['cate_id'] = r['PicInfo']['CategoryId']
    r['obj_region'] = r['PicInfo']['Region']
    return r

def my_image_search_for_category_detection_display(requestClient, instanceName, image_path):
    r = my_image_search_for_category_detection(requestClient, instanceName, image_path)
    display(my_image_preview(image_path, r['obj_region'], r['cate_desc']))
    #print(image_path, ' | found category_desc: ', r['cate_desc'], r['cate_id'], ' | found category_id: ', r['cate_id'], ' | found region: ',  r['obj_region'])
    return r

###########################################################
###########################################################
def my_image_search_for_similarity(requestClient, instanceName, image_path):
    r = my_image_search_base(requestClient, instanceName, image_path)
    #r = json.loads(r)
    #pprint(r)
    category_desc = ''
    for c in r['PicInfo']['AllCategories']:
        if r['PicInfo']['CategoryId'] == c['Id']:
            category_desc = c['Name']
    #pprint(r)

    #print(image_path, 'found category_desc: ', category_desc, r['PicInfo']['Category'], 'found category_id: ', r['PicInfo']['Category'], 'found region: ',  r['PicInfo']['Region'])

    #image_similar_name = r['Auctions'][1]['PicName']
    #image_similar_path = r['Auctions'][1]['CustomContent']
    image_similar_name = json.loads(r['Auctions'][1]['CustomContent'])['name']
    image_similar_path = json.loads(r['Auctions'][1]['CustomContent'])['path']
    image_similar_score = r['Auctions'][1]['SortExprValues']
    category_desc = json.loads(r['Auctions'][1]['CustomContent'])['cate_desc']
    obj_region = json.loads(r['Auctions'][1]['CustomContent'])['obj_region']
    print(image_path)
    print("similar score: ", image_similar_score, "similar image: ", image_similar_path)

    #print(r['Auctions']['Auction'][1])

    img1 = my_image_preview(image_path, obj_region, category_desc)
    img2 = my_image_preview(image_similar_path, '0,0,0,0', 'most_similart_to', 'green')

    img_height = img1.size[1]
    if img1.size[1] < img2.size[1]:
        img_height = img2.size[1]
    img = Image.new('RGB', (img1.size[0]+img2.size[0]+40, img_height), "white")
    img.paste(img1, (0, 0))
    img.paste(img2, (img1.size[0]+40, 0))
    #print(img)

    draw = ImageDraw.Draw(img)
    draw.text((img1.size[0]+20, img_height/2), '=>', fill="green")
    draw.text((img1.size[0]+20, img_height/2+10), 'most', fill="red")
    draw.text((img1.size[0]+20, img_height/2+20), 'similar', fill="red")
    draw.text((img1.size[0]+20, img_height/2+30), 'to', fill="red")
    draw.text((img1.size[0]+20, img_height/2+40), '=>', fill="green")
    sim_score = "{0:.0%}".format(float(image_similar_score.split(';')[0]))
    draw.text((img1.size[0]+20, img_height/2+50), image_similar_score, fill="red")

    #display(img1)
    #display(img2)
    display(img)

    return r


# coding: utf-8

# In[ ]:

class Weighted_Graph():
    """Класс взвешенных графов. Для представления графа используется список ребер

       Атрибуты:
       :self.v: int - количество вершин
       :self.graph: list - список ребер

       Функции:
       :add_edge(u,v,w): добавляет ребро весом w между вершинами u и v
       :ford_bellman_algo(s = 0): list - возвращает минимальные расстояния от вершины s до остальных вершиню при наличии
       ребер отрицательного веса - возвращает х
       :list_to_dict(Oriented = False): - меняет представление графа из списка ребер в словарь словарей
       :components_of_connection: int - возвращает колличество компонент связанности, работает только с представлением
       графа в виде словаря словарей
       """
    def __init__(self, vert):

        self.v = vert
        self.graph = []

    def add_edge(self, u, v, w=1):

        self.graph.append((u-1, v-1, w))

    def ford_bellman_algo(self, s = 0):

        m = len(self.graph)

        d = [None]*self.v #заводим массив для записи результатов
        d[s] = 0 #путь до стартовой вершины равен 0
        b = float('inf')

        for i in range(self.v - 1):
            for u, v, w in self.graph:
                if d[u] is not None:
                    d[v] = min(b if d[v] is None else d[v], d[u] + w) # релаксация

        graph = list(d)

        #проверка на ребра отрицательного веса
        for u, v, w in self.graph: #проводим еще одну фазу
            #print('--->', u,v,w)
            if d[u] is not None:
                d[v] = min(b if d[v] is None else d[v], d[u] + w)

        #сравниваем, поменялись ли стоимости путей
        for i in range(self.v):
            if d[i] != graph[i] or d[i] == None:
                d[i] = 'x'

        return d

    def list_to_dict(self, Oriented = False):
        graph = {i:{} for i in range(self.v)}
        for u, v, w in self.graph:
            graph[u].update({v: w})
            if not Oriented:
                graph[v].update({u: w})
        self.graph = graph

    def components_of_connection(self):
        visited = [False]*self.v  # создаём массив посещённых вершины длины n, заполненный false изначально

        def dfs(u: int):
            visited[u] = True
            for v in self.graph[u].keys():
                if not visited[v]:
                    dfs(v)
        n_comp = 0
        for i in range(1, self.v):
            if not visited[i]:
                dfs(i)
                n_comp+=1
        return n_comp

    if __name__ == "__main__":
        string = input('Enter the string ')
        pattern = input('Enter the substring ')
        print(' '.join(map(str, FindSubString(string, pattern))))