import locale
import time as time

# Adds local display settings
my_locale = locale.setlocale(locale.LC_ALL, "")

FILE_1 = "Haschcode 2021\\Practice round\\Input\\a_example"
FILE_2 = "Haschcode 2021\\Practice round\\Input\\b_little_bit_of_everything.in"
FILE_3 = "Haschcode 2021\\Practice round\\Input\\c_many_ingredients.in"
FILE_4 = "Haschcode 2021\\Practice round\\Input\\d_many_pizzas.in"
FILE_5 = "Haschcode 2021\\Practice round\\Input\\e_many_teams.in"

LISTE_FILE = [FILE_1, FILE_2, FILE_3, FILE_4, FILE_5]

class Main():

    """Class allowing the reading of files"""

    def __init__(self, file: str) -> None:
        """
        Initialization of the class.

        Args:
            file (file text): Input file
        """
        self.file = file
        self.scrore = int()
        self.num_people = int()  # Total number of persons
        self.num_order = int()  # Number of teams delivered
        self.num_pizza_order = int()  # Number of pizzas delivered

        start = time.time()
        self.lecture()
        self.resolution()
        self.time = time.time() - start
        self.write_answer()
        self.write_detail()

        print(self)

    def lecture(self) -> None:
        """
        Reading the file.
        """
        k = int()
        self.list_teams = list()
        self.list_pizzas = list()

        f = open(self.file, encoding='ASCII')
        ligne = f.readline().strip().split()
        num_pizza = int(ligne[0])
        # Creation of teams
        for i in range(1, len(ligne)):
            for j in range(0, int(ligne[i])):
                self.list_teams.append(Team(k + j, i+1))
            k += i
        # Sorting according to the number of menbre
        self.list_teams.sort(key=lambda k: k.num_people, reverse=True)

        # Creation of pizzas
        for i in range(0, num_pizza):
            ligne = f.readline().strip().split()
            self.list_pizzas.append(Pizza(i, ligne[0], ligne))
        # Sorting according to the number of ingredients
        self.list_pizzas.sort(key=lambda k: k.num_ingredients, reverse=True)

        # Data for display
        self.num_pizza = len(self.list_pizzas)
        self.num_teams = len(self.list_teams)
        for team in self.list_teams:
            self.num_people += team.num_people

    def resolution(self) -> None:
        """
        Solving the problem.
        """
        self.team_order = list()
        max_ingredient = int(self.list_pizzas[0].num_ingredients)

        # First step
        # Max number of ingredients in common per pizza
        for min_score in range(0, max_ingredient + 1):
            for pizza in self.list_pizzas:  # Pizza is looking for a team
                for team in self.list_teams:
                    # If the number of ingredients
                    # in common is less than min_score
                    if len(team.list_order) < team.num_people and \
                            team.order(pizza) <= min_score:
                        team.confirm_order(pizza)
                        # It is withdrawn from the pizzas available
                        self.list_pizzas.remove(pizza)
                        break
                    # The team has no more place
                    elif len(team.list_order) == team.num_people:
                        # It is withdrawn from the teams available
                        self.list_teams.remove(team)
                        # It is added to the teams that have ordered
                        self.team_order.append(team)
            self.list_teams.sort(key=lambda k: k.score,
                                 reverse=True)   # Sort by score

        # Second step
        # We add all the pizzas where there's room left over
        for team in self.list_teams:
            while len(team.list_order) < team.num_people and len(self.list_pizzas) > 0:
                team.list_order.append(self.list_pizzas[0])
                del self.list_pizzas[0]
            if len(team.list_order) == team.num_people:
                self.list_teams.remove(team)
                self.team_order.append(team)

        # We remove all teams without pizzas
        try:
            while len(self.list_teams[-1].list_order) == 0:
                del self.list_teams[-1]
        except IndexError:
            pass

        # Third step
        # The teams with the lowest score give the teams with the highest score
        # to fill the remaining place
        for team in self.list_teams:
            if len(self.list_teams[-1].list_order) == 0:
                del self.list_teams[-1]
            else:
                for pizza in self.list_teams[-1].list_order:
                    self.list_teams[-1].list_order.remove(pizza)
                    team.confirm_order(pizza)
                    if len(team.list_order) == team.num_people:
                        self.team_order.append(team)
                        self.list_teams.remove(team)
                        break

        # Data for display
        self.num_order = len(self.team_order)
        for team in self.team_order:
            self.num_pizza_order += len(team.list_order)

    def write_answer(self) -> None:
        """
        Writes the response file.
        """
        chaine = str()
        f = open("Haschcode 2021\\Practice round\\Output\\" +
                 self.file[36:] + ".txt", "w")
        f.write(str(len(self.team_order)) + "\n")
        for team in self.team_order:
            chaine += str(team.num_people) + " "
            for pizza in team.list_order:
                chaine += str(pizza.num_pizza) + " "
            self.scrore += len(team.list_ingredients)**2
            f.write(chaine + "\n")
            chaine = str()

    def write_detail(self) -> None:
        chaine = str()
        chaine += self.__str__()
        f = open("Haschcode 2021\\Practice round\\Output Details\\" +
                 self.file[36:] + ".txt", "w", encoding="UTF8")
        for team in self.team_order:
            chaine += str(team)
        f.write(chaine + "\n")

    def __str__(self) -> str:
        """
        Generates a character string with class information.

        Returns:
            str: Character string with class information
        """
        chaine = 80 * "-"
        chaine += "\n" + self.file[36:] + "\n"
        chaine += "\nThere are {:n} teams with {:n} people.".format(
            self.num_teams, self.num_people)
        chaine += "\n{:n} teams were delivered with {:n} pizzas.".format(
            self.num_order, self.num_pizza_order)
        chaine += "\nThere are {:n} differents ingredients.".format(
            self.scrore**(1/2))
        chaine += "\nExecution time: " + \
            time.strftime("%Hh %Mmin %Ss", time.gmtime(self.time))
        chaine += "\nScore: {:n}\n".format(self.scrore) + "\n"
        return chaine

class Team():
    """
    Team building class.
    """

    def __init__(self, num_team: int, num_people: int) -> None:
        """
        Initialization of the class.

        Args:
            num_team (int): team number
            num_people (int): number of people
        """
        self.num_team = int(num_team)
        self.num_people = int(num_people)
        self.list_order = list()  # List of pizzas ordered
        self.list_ingredients = list()  # List of ingredients
        self.score = int()

    def order(self, pizza) -> None:
        """
        We look at how many points the order earns.

        Args:
            pizza (Pizza): the pizza ordered

        Returns:
            int: points reported by the order
        """
        score = int()
        for ingredient in pizza.ingredients:
            if ingredient in self.list_ingredients:
                score += 1
        return score

    def confirm_order(self, pizza) -> None:
        """
        We confirm that we take this pizza.

        Args:
            pizza (Pizza): the pizza ordered
        """
        self.list_order.append(pizza)
        for ingredient in pizza.ingredients:  # New ingredients are registered
            if ingredient not in self.list_ingredients:
                self.list_ingredients.append(ingredient)
        self.score = len(self.list_ingredients)

    def __str__(self) -> str:
        """
        Generates a character string with class information.

        Returns:
            str: Character string with class information
        """
        chaine = "Team n°{:n} with {:n} members, ordered {:n} pizzas:\n".format(
            self.num_team, self.num_people, len(self.list_order))
        for pizza in self.list_order:
            chaine += "\t" + str(pizza) + "\n"
        chaine += "There are {:n} differents ingredients.\n\n".format(
            len(self.list_ingredients))
        return chaine

class Pizza():
    """
    Pizza creation class.
    """

    def __init__(self, num_pizza: int, num_ingredients: int,
                 ingredients: list) -> None:
        """
        Initialization of the class.

        Args:
            num_pizza (int): number of pizzas ordered
            num_ingredients (int): number of differents ingredients
            ingredients (list): list of differents ingredients
        """
        self.num_pizza = num_pizza
        self.num_ingredients = int(num_ingredients)
        del ingredients[0]  # The number of ingredients is removed
        self.ingredients = ingredients

    def __str__(self) -> str:
        """
        Generates a character string with class information.

        Returns:
            str: Character string with class information
        """
        chaine = "Pizza n°{:n} with {:n} ingredients:\n\t\t".format(
            self.num_pizza, self.num_ingredients)
        for ingredient in self.ingredients:
            chaine += ingredient + ", "
        return chaine

if __name__ == "__main__":
    score = int()
    temps = int()
    print(80 * "#")
    for file in LISTE_FILE:
        main = Main(file)
        score += main.scrore
        temps += main.time

    print("\nTotal score: {:n}".format(score), "\nTemps total: ",
          time.strftime("%Hh %Mmin %Ss", time.gmtime(temps)))
    print(80 * "#")


"""
Internal. Functions to use SQL on Tables.
"""

import sqlite3

import pyarrow as pa

from . import Schema, Table

TYPES_MAPPING = {
    pa.bool_(): "INTEGER",
    pa.int8(): "INTEGER",
    pa.int16(): "INTEGER",
    pa.int32(): "INTEGER",
    pa.int64(): "INTEGER",
    pa.uint8(): "INTEGER",
    pa.uint16(): "INTEGER",
    pa.uint32(): "INTEGER",
    pa.uint64(): "INTEGER",
    pa.float16(): "REAL",
    pa.float32(): "REAL",
    pa.float64(): "REAL",
    pa.binary(): "BLOB",
    pa.string(): "TEXT",
}

def create_table(schema: Schema, table: Table) -> sqlite3.Connection:
    """Create an SQLite database with a single table named "data", with a
    schema derived from a given file schema, and given file content loaded
    into it. Returns an SQLite connection instance."""
    connection = _create_table(schema)
    _load_table(connection, schema, table)
    return connection

def query(connection: sqlite3.Connection, query: str) -> Table:
    table: Table = {}
    cursor = connection.execute(query)
    for row in cursor:
        for column_name in row.keys():
            table.setdefault(column_name, []).append(row[column_name])
    return table

def _create_table(schema: Schema) -> sqlite3.Connection:
    schema_table, _ = schema  # this function doesn't use the metadata
    fields = [
        (name, _sql_type(pa_type))
        for name, pa_type in zip(schema_table["Name"], schema_table["Type"])
    ]
    fields_declaration = ", ".join(
        [f"{name} {sql_type}" for name, sql_type in fields]
    )
    connection = sqlite3.connect(":memory:")
    connection.row_factory = sqlite3.Row
    connection.execute(f"CREATE TABLE data ( {fields_declaration} )")
    return connection

def _load_table(connection: sqlite3.Connection, schema: Schema, table: Table):
    schema_table, _ = schema  # this function doesn't use the metadata
    field_names = ", ".join(schema_table["Name"])
    placeholders = ", ".join("?" * len(schema_table["Name"]))
    insert_query = (
        f"INSERT INTO data ( {field_names} ) VALUES ( {placeholders } )"
    )
    connection.executemany(insert_query, zip(*table.values()))

def _sql_type(pa_type) -> str:
    return TYPES_MAPPING.get(pa_type, "TEXT")


#This is a number guessing game
import random
secretnum = random.randint(1,20)
print('Hello what is your name?')
name = input()
print('Hello ' + name +" I'm thinking of a number between 1 and 20")

for guesstaken in range(1,7):
    print('Take a guess')
    guess = int(input())
    if guess < secretnum :
        print('Sorry too low, try again.')
    elif guess > secretnum :
        print('Sorry too high, try again')
    else:
        break
if guess == secretnum :
    print('Good job you got it')
else :
    print('Sorry the answer was' + secretnum)


import os
import traceback
import yaml
from Common import common_function
from Common.log import log

class Report:
    def __init__(self, case_name):
        self.result = "Pass"
        self.script_complete_path = common_function.OSTool.get_current_dir()
        self.case_name = case_name
        self.ip = common_function.OSTool.get_ip()
        self.uut_name = self.ip
        self.report_path = self.get_report_path()
        self.steps_value_list = []

    def get_report_path(self):
        report_path = "{0}/Test_Report/{1}.yaml".format(self.script_complete_path, self.ip)
        return report_path

    def reporter(self, step_name='', result='', expect='', actual='', note=''):
        if result.upper() == "FAIL":
            self.result = "FAIL"
        dic = {"step_name": step_name, "result": result, "expect": expect, "actual": actual,
               "note": note}  # The dic of one step
        self.steps_value_list.append(dic)
        return self.steps_value_list

    def generate(self):
        report_data = []
        case_dic = {"case_name": self.case_name, "result": self.result, "steps": self.steps_value_list,
                    "uut_name": self.uut_name}  # The data of one case.
        report_data.append(case_dic)

        if not os.path.exists(self.report_path):
            self.write_data_to_yaml(report_data)  # Write data to yaml file
        else:
            original_data = self.get_original_data()
            if original_data is not None:
                new_data = original_data + report_data
            else:
                new_data = report_data
            self.write_data_to_yaml(new_data)  # Write data to yaml file

    def write_data_to_yaml(self, data):
        with open(self.report_path, 'w', encoding='utf-8') as f:
            yaml.safe_dump(data, f)

    def get_original_data(self):
        with open(self.report_path, 'r', encoding='utf-8') as f:
            original_data = yaml.safe_load(f)
            return original_data

class ScriptReport:
    def __init__(self, file):
        self.report_file = file
        if not os.path.exists(self.report_file):
            f = open(self.report_file, 'w')
            f.close()

    def new_case_result(self, case_name, step_names=None):
        if step_names is None:
            step_names = []
        steps = []
        for step_name in step_names:
            steps.append(
                {'step_name': step_name,
                 'result': 'Fail',
                 'expect': '',
                 'actual': '',
                 'note': ''}
            )
        result = [{'case_name': case_name,
                   'uut_name': common_function.check_ip_yaml(),
                   'result': 'Fail',
                   'steps': steps
                   }]
        with open(self.report_file, 'r') as f:
            current_report = yaml.safe_load(f)
        if isinstance(current_report, list):
            for report in current_report:
                if report['case_name'] == case_name:
                    return
            current_report.extend(result)
        else:
            current_report = result
        with open(self.report_file, 'w') as f:
            yaml.safe_dump(current_report, f)

    def update_case_result(self, case_name, step):
        """
        :param file:
        :param case_name:
        :param step: {'step_name': step1, 'result': Pass, 'note': '', expect:'', actual:''}
        :return:
        """
        with open(self.report_file, 'r') as f:
            current_report = yaml.safe_load(f)
        for report in current_report:
            if report['case_name'] == case_name:
                if self.__check_step_exist(report['steps'], step['step_name']):
                    report['steps'] = self.__update_step_result(report['steps'], step)
                    case_status = True
                    for sub_step in report['steps']:
                        if sub_step['result'].upper() == 'FAIL':
                            case_status = False
                            break
                    if case_status:
                        report['result'] = 'Pass'
                    else:
                        report['result'] = 'Fail'
                    break
                else:
                    report['steps'].append(step)
                    case_status = True
                    for sub_step in report['steps']:
                        if sub_step['result'].upper() == 'FAIL':
                            case_status = False
                            break
                    if case_status:
                        report['result'] = 'Pass'
                    else:
                        report['result'] = 'Fail'
                    break

        with open(self.report_file, 'w') as f:
            yaml.safe_dump(current_report, f)

    @staticmethod
    def __update_step_result(steps_rs, step: dict):
        for step_rs in steps_rs:
            if step_rs['step_name'] == step['step_name']:
                step_rs['result'] = step.get('result', step_rs['result'])
                step_rs['expect'] = step.get('expect', step_rs['expect'])
                step_rs['actual'] = step.get('actual', step_rs['actual'])
                step_rs['note'] = step.get('note', step_rs['note'])
                return steps_rs

    @staticmethod
    def __check_step_exist(steps, step_name):
        for step in steps:
            if step['step_name'] == step_name:
                return True
        return False

    def check_step(self, func, case_name, step_name, expect='', note='', **kwargs):
        """
        pass a func(step) to this method, we'll automatically update case result to ip.yaml(file)
        :param note:
        :param expect:
        :param step_name:
        :param case_name:
        :param func: step method, func should return [bool, str], bool: pass fail, str: actual information
        :param kwargs: parameters of method func
        :return:
        """
        try:
            result = func(**kwargs)
        except:
            log.error(f'[report][script_report]{case_name} {step_name} meet exception:\n{traceback.format_exc()}',
                      common_function.OSTool.get_current_dir('Test_Report', 'img', '{}_{}.jpg'.format(case_name, step_name)))
            step = {'step_name': step_name,
                    'result': 'Fail',
                    'expect': expect,
                    'actual': r'img\{}'.format(common_function.OSTool.get_current_dir('Test_Report',
                                                                               'img',
                                                                               '{}_{}.jpg'.format(case_name,
                                                                                step_name))),
                    'note': 'meet exception'}
            self.update_case_result(case_name, step)
            return False
        if not result[0]:
            log.error(f'[report][script_report]{result[1]}',
                      common_function.OSTool.get_current_dir('Test_Report', 'img', '{}_{}.jpg'.format(case_name, step_name)))
            step = {'step_name': step_name,
                    'result': 'Fail',
                    'expect': expect,
                    'actual': r'img\{}'.format(common_function.OSTool.get_current_dir('Test_Report',
                                                                               'img',
                                                                               '{}_{}.jpg'.format(case_name,
                                                                                step_name))),
                    'note': note}
            self.update_case_result(case_name, step)
            return False
        else:
            log.info(f'[report][script_report]{result[1]}')
            step = {'step_name': step_name,
                    'result': 'Pass',
                    'expect': expect,
                    'actual': result[1],
                    'note': note}
            self.update_case_result(case_name, step)
            return True

    def check_step_reboot(self, case_name, steps: dict):  # steps: {'step_name': method, ..}
        pass


import pygame
import random

BLACK=(0,0,0)
WHITE=(255,255,255)
GREEN=(0,255,0)
RED=(255,0,0)
BLUE=(0,0,255)

file_write = open("class_feature2_4.dat",'w')

colors = [RED, BLUE]
points = []
points_colors = []
curr_color_ind = 0;

def draw_obj(screen, cx, cy):
	for ii in range(len(points)):
		pygame.draw.circle(screen, points_colors[ii], points[ii], 3)

pygame.init()

#width and height of screen
size=(500, 500)
screen= pygame.display.set_mode(size)

pygame.display.set_caption("Generate Data")

done = False;

while not done:

	mousex, mousey = pygame.mouse.get_pos()

	for event in pygame.event.get():
	    if event.type==pygame.QUIT:
	        done=True

	    if event.type==pygame.KEYDOWN:
	    	# Change color to next if spacebar is pressed
	    	if event.key==pygame.K_SPACE:
		        curr_color_ind = (curr_color_ind+1)%len(colors)

	    if event.type == pygame.MOUSEBUTTONDOWN:
	    	points.append((mousex, mousey))
	    	points_colors.append(colors[curr_color_ind])

	    if event.type == pygame.MOUSEBUTTONUP:
	    	print(len(points))
	    	px = mousex/500.0
	    	py = (500-mousey)/500.0
	    	file_write.write(str(px) + " " + str(py) + " " + str(curr_color_ind))
	    	file_write.write("\n")

	screen.fill(WHITE)
	draw_obj(screen, mousex, mousey)
	pygame.display.flip()

pygame.quit()
file_write.close()


"""
Mark errors in the results file
"""
import openpyxl
import re
import os
import math
import numpy as np
import pandas as pd
from mtl import verbalnumbers

import mtl.verbalnumbers.hebrew as hebnum

lexical_classes = hebnum.ones, hebnum.tens, hebnum.hundreds, hebnum.thousands

# noinspection PyMethodMayBeStatic
class ErrorAnalyzer(object):

    #------------------------------------------------------
    def __init__(self, digit_mapping=None, unknown_response_chars=('-', '?'),
                 subj_id_transformer=None, consider_thousand_as_digit=True, accuracy_per_digit=False,
                 fail_on_segment_order_error=False, subj_id_in_xls=True, in_col_names=None, phonological_error_flds=(),
                 set_per_subject=None, save_verbal_response=False):
        """

        :param phonological_error_flds: List of xls columns which contain number of phonological errors. All these columns will be summed.
        :param digit_mapping:
        :param subj_id_transformer: Function for transfoming the subject ID field
        :param consider_thousand_as_digit:  Whether the word "thousand" should count towards digit errors also in numbers with 5 or 6 digits
        :param accuracy_per_digit: This concerns the output files per word/morpheme: whether to compute the word/digit accuracy
                for each specific word or less precisely. Value=True is impossible if the target contains duplicate digits.
        """
        self._digit_mapping = {str(d): d for d in range(0, 10)}
        if digit_mapping is not None:
            self._digit_mapping.update(digit_mapping)

        self.subj_id_transformer = subj_id_transformer
        self.consider_thousand_as_digit = consider_thousand_as_digit
        self.accuracy_per_digit = accuracy_per_digit
        self.fail_on_segment_order_error = fail_on_segment_order_error
        self.phonological_error_flds = phonological_error_flds
        self.subj_id_in_xls = subj_id_in_xls
        self.save_verbal_response = save_verbal_response
        self.unknown_response_chars = unknown_response_chars

        self.in_col_names = dict(block='Block', condition='Condition', itemnum='ItemNum', target='target', response='response',
                                 nwords='NWordsPerTarget', exclude='exclude', manual='manual')
        if save_verbal_response:
            self.in_col_names['VerbalResponse'] = 'verbal response'
        if in_col_names is not None:
            for k, v in in_col_names.items():
                assert k in self.in_col_names, 'Invalid in_col_names - unexpected column "{}"'.format(k)
                self.in_col_names[k] = v

        optional_col_keys = 'exclude', 'manual'

        self.xls_mandatory_cols = tuple(c for k, c in self.in_col_names.items() if c is not None and k not in optional_col_keys)
        self.xls_optional_cols = tuple(c for k, c in self.in_col_names.items() if c is not None and k in optional_col_keys)
        self.xls_cols = (('Subject',) if subj_id_in_xls else ()) + self.xls_mandatory_cols
        self.xls_out_cols = ('Subject',)+self.xls_mandatory_cols+self.xls_optional_cols+ \
                            ('NTargetDigits', 'NMissingWords', 'PMissingWords', 'NMissingDigits',
                             'PMissingDigits', 'NMissingClasses', 'PMissingClasses', 'PMissingMorphemes')

        if len(self.phonological_error_flds) > 0:
            self.xls_out_cols += 'NPhonologicalErrors',

        if set_per_subject is None:
            self.fixed_value_per_subject = {}
        else:
            self.fixed_value_per_subject = { sid: {} for sid in set_per_subject['subjid'] }
            for i, sid in enumerate(set_per_subject['subjid']):
                self.fixed_value_per_subject[sid] = {cn: set_per_subject[cn][i] for cn in set_per_subject.keys() if cn != 'subjid'}
            self.xls_out_cols += tuple(cn for cn in set_per_subject.keys() if cn != 'subjid')

    #------------------------------------------------------
    def run_for_worksheet(self, in_fn, worksheet='data', out_dir=None, out_fn_prefix='data_coded'):
        """
        Analyze the error rates (digit, class, morpheme, word) in each trial

        :param in_fn: Excel file with the raw data (uncoded)
        :param worksheet: Name of worksheet to read
        :param out_dir: Directory for output files
        :param out_fn_prefix:
        """
        self.run_for_worksheets(in_fn, [worksheet], out_dir, out_fn_prefix)

    #------------------------------------------------------
    def run_for_worksheets(self, in_fn, worksheets=None, out_dir=None, out_fn_prefix='data_coded'):
        """
        Analyze the error rates (digit, class, morpheme, word) in each trial

        :param set_per_subject: values to set for each participant. This is a dict with a 'subjid' entry and
                                one additional entry for each column to set
        """

        out_wb, out_ws = self.create_output_workbook()
        wb = openpyxl.load_workbook(in_fn)
        if worksheets is None:
            worksheets = [ws.title for ws in wb.worksheets]

        result_per_word = []
        n_excluded = []
        n_phonerr = []

        ok = True
        out_row_num = 2

        for worksheet in worksheets:
            print('\nProcessing worksheet "{}"...'.format(worksheet))

            try:
                in_ws, col_inds = self._open_worksheet(wb, worksheet, in_fn)
            except ValueError as e:
                print('>>> ERROR (worksheet ignored): {}'.format(e))
                continue

            found_empty_rows = False
            subj_n_excluded = 0
            subj_n_phonerr = 0

            if 'manual' in self.in_col_names:
                nrep = sum('repeat' in str(in_ws.cell(rownum, col_inds[self.in_col_names['manual']]).value) for rownum in range(2, in_ws.max_row+1))
                if nrep > 0:
                    print('{}: {} excluded&repeated trials'.format(worksheet.title(), nrep))

            for rownum in range(2, in_ws.max_row+1):
                if self.fixed_value_per_subject is not None and worksheet in self.fixed_value_per_subject:
                    self.set_fixed_values(self.fixed_value_per_subject[worksheet], out_ws, out_row_num)

                rc = self.parse_row(in_ws, out_ws, rownum, out_row_num, col_inds, result_per_word, worksheet)

                if rc == 'empty':
                    found_empty_rows = True
                    continue

                elif rc == 'excluded':
                    subj_n_excluded += 1

                elif rc == 'error':
                    ok = False

                else:
                    subj_n_phonerr += rc
                    out_row_num += 1

                    if found_empty_rows:
                        print('ERROR: row {} in worksheet "{}" contains data but there were few empty rows previously. Skipped.'.
                              format(rownum, worksheet))
                        ok = False

            n_excluded.append(subj_n_excluded)
            n_phonerr.append(int(subj_n_phonerr))

            #-- Last row is not needed
            out_ws.delete_rows(out_row_num)

        if ok:
            print('{} rows were processed, no errors found.'.format(out_row_num-1))
        else:
            print('Some errors were encountered.')

        out_ws.freeze_panes = out_ws['A2']
        self.auto_col_width(out_ws)

        if out_dir is None:
            print('WARNING: results were not saved for {}'.format(in_fn))

        else:
            out_wb.save(out_dir + os.sep + out_fn_prefix + '.xlsx')
            pd.DataFrame(result_per_word).to_csv(out_dir + os.sep + out_fn_prefix + '_words.csv', index=False)

            subjstat = pd.DataFrame(dict(subject=worksheets, n_excluded=n_excluded))
            if len(self.phonological_error_flds) > 0:
                subjstat['n_phonerr'] = n_phonerr
            subjstat.to_csv(out_dir + os.sep + out_fn_prefix + '_subjstat.csv', index=False)

    #------------------------------------------------------
    def parse_row(self, in_ws, out_ws, rownum, out_rownum, col_inds, result_per_word, worksheet):
        """
        Parse a single row, copy it to the output file
        Return: the number of phonological errors; or a string reflecting the error/issue
        """

        if self.in_col_names['exclude'] in col_inds:
            exclude = in_ws.cell(rownum, col_inds[self.in_col_names['exclude']]).value
            if exclude == 1:
                return 'excluded'

        if self.subj_id_in_xls:
            subj_id = in_ws.cell(rownum, col_inds[self.in_col_names['subject']]).value
        else:
            subj_id = worksheet

        raw_target = in_ws.cell(rownum, col_inds[self.in_col_names['target']]).value
        raw_response = in_ws.cell(rownum, col_inds[self.in_col_names['response']]).value

        if self.in_col_names['nwords'] is None:
            n_target_words = None
        else:
            n_target_words = in_ws.cell(rownum, col_inds[self.in_col_names['nwords']]).value

            if n_target_words is None:
                print("Error in line {}: 'NWordsPerTarget' was not specified".format(rownum))
                return 'error'

        if raw_target is None:
            return 'empty'

        #-- Save basic columns

        subj_id = subj_id if self.subj_id_transformer is None else self.subj_id_transformer(subj_id)
        self._save_value(out_ws, out_rownum, 'Subject', subj_id)
        if self.save_verbal_response:
            vr_col = self.in_col_names['VerbalResponse']
            self._save_value(out_ws, out_rownum, vr_col, in_ws.cell(rownum, col_inds[vr_col]).value)

        copy_cols = self.xls_mandatory_cols + tuple([c for c in self.xls_optional_cols if self.in_col_names[c] in col_inds])
        for colname in copy_cols:
            self._save_value(out_ws, out_rownum, colname, in_ws.cell(rownum, col_inds[colname]).value)

        #-- Analyze target & response

        target, target_segments = self.parse_target(raw_target, rownum)

        if n_target_words is None:
            n_target_words = len(target)
        elif n_target_words != len(target):
            print("Error in line {}: Invalid number of words ({}), expecting {} words".format(rownum, len(target), n_target_words))
            return 'error'

        target_word_said, target_digit_said, n_class_errs = self.analyze_response(raw_response, target, target_segments, rownum)
        if target_word_said is None:
            return 'error'

        n_word_errs = sum([digsaid is False for digsaid in target_word_said])
        n_digit_errs = sum([digsaid is False for digsaid in target_digit_said])

        #-- Phonological errors
        if len(self.phonological_error_flds) > 0:
            n_phonerr = [in_ws.cell(rownum, col_inds[c]).value for c in self.phonological_error_flds]
            n_phonerr = [n for n in n_phonerr if not _isnull(n)]
            if sum(not isinstance(n, (int, float)) for n in n_phonerr) > 0:
                print('Error in line {}: invalid number of phonological errors ({})'.format(rownum, n_phonerr))
                n_phonerr = 0
            else:
                n_phonerr = sum(n_phonerr)
                self._save_value(out_ws, out_rownum, 'NPhonologicalErrors', n_phonerr)

        else:
            n_phonerr = 0

        #-- Save performance measures

        n_target_digits = sum([t.digit is not None for t in target])

        self._save_value(out_ws, out_rownum, 'NMissingWords', n_word_errs)
        self._save_value(out_ws, out_rownum, 'NMissingDigits', n_digit_errs)
        self._save_value(out_ws, out_rownum, 'NMissingClasses', n_class_errs)

        self._save_value(out_ws, out_rownum, 'NTargetDigits', n_target_digits)
        self._save_value(out_ws, out_rownum, 'PMissingWords', n_word_errs / n_target_words)
        self._save_value(out_ws, out_rownum, 'PMissingDigits', n_digit_errs / n_target_digits)
        self._save_value(out_ws, out_rownum, 'PMissingClasses', n_class_errs / n_target_words)
        self._save_value(out_ws, out_rownum, 'PMissingMorphemes', (n_class_errs + n_digit_errs) / (n_target_words + n_target_digits))

        self.custom_process_row(in_ws, out_ws, rownum, out_rownum, col_inds)

        self._save_accuracy_per_word(subj_id, in_ws, rownum, col_inds, target, target_word_said, target_digit_said, raw_response, raw_target,
                                     result_per_word)

        return n_phonerr

    #------------------------------------------------------------------------------
    def custom_process_row(self, in_ws, out_ws, in_rownum, out_rownum, col_inds):
        pass

    #------------------------------------------------------------------------------
    def parse_target(self, raw_target, rownum):
        target_segments = self.parse_target_or_response(raw_target, rownum)
        target = np.array(self.collapse_segments(target_segments)[::-1])  # put the ones word in position 1
        return target, target_segments

    #------------------------------------------------------------------------------
    def _save_accuracy_per_word(self, subj_id, in_ws, rownum, col_inds, target, target_word_said, target_digit_said,
                                raw_response, raw_target, result_per_word):

        block = None if self.in_col_names['block'] is None else in_ws.cell(rownum, col_inds[self.in_col_names['block']]).value
        cond_name = in_ws.cell(rownum, col_inds['Condition']).value
        item_num = in_ws.cell(rownum, col_inds['ItemNum']).value
        n_target_words = len(target_word_said)

        for i, (word_said, digit_said, target_word) in enumerate(zip(target_word_said, target_digit_said, target)):

            lexical_class_order = lexical_classes.index(target_word.lexical_class) if target_word.lexical_class in lexical_classes else ''

            r = dict(subject=subj_id,
                     block=block,
                     condition=cond_name,
                     item_num=item_num,
                     n_target_words=n_target_words,
                     target=raw_target,
                     response=raw_response,
                     word_order=n_target_words-i,
                     word_class=target_word.lexical_class,
                     word_class_order=lexical_class_order,
                     target_word=target_word,
                     word_ok=1 if word_said else 0,
                     digit_ok=None if digit_said is None else 1 if digit_said else 0,
                     )

            result_per_word.append(r)

    #------------------------------------------------------
    def analyze_response(self, raw_response, target, target_segments, rownum):
        """
        Analyze the target-response matching and save the results onto the Excel worksheet

        Returns:
        - a bool array with one entry per target word, indicating whether that word was said
        - a bool array with one entry per target word, indicating whether the word's digit was said (None if the target word has no digit)
        - The number of unsaid target classes

        :param raw_response: The response as a string
        :param target:
        :param target_segments:
        :param rownum:
        """

        response_segments = self.parse_response(raw_response, rownum, target_segments)
        if target_segments is None or response_segments is None:
            return [None] * 3

        response = self.collapse_segments(response_segments)

        target_word_said, target_digit_said = self.target_items_said(target, response)

        n_unsaid_target_classes = self._n_missing_classes(target, response)

        return target_word_said, target_digit_said, n_unsaid_target_classes

    #------------------------------------------------------
    def _save_value(self, out_ws, rownum, colname, value):
        out_ws.cell(rownum, 1 + self.xls_out_cols.index(colname)).value = value

    #------------------------------------------------------
    def _n_missing_classes(self, target, response):
        target = [t.lexical_class for t in target]
        response = [r.lexical_class for r in response]

        for r in response:
            if r in target:
                target.remove(r)

        return len(target)

    #------------------------------------------------------
    def target_items_said(self, target_words, response_words):
        """ Return an array of bool: for each target item, whether it was said or not """

        #---- Step 1: fully-correct words

        tmp_target_words = list(target_words)
        response_words = list(response_words)

        #-- Loop through response, mark each said word
        for i, resp in enumerate(response_words):
            if resp is None:
                continue

            try:
                tmp_target_words[tmp_target_words.index(resp)] = None
                response_words[i] = None
            except ValueError:
                #-- Exception from target_words.index() - i.e., response word was not said
                pass

        target_word_said = [t is None for t in tmp_target_words]

        #---- Step 2: digits said with incorrect class

        target_digit_said = list(target_word_said)
        for i, t in enumerate(target_words):
            if t.digit is None:
                target_digit_said[i] = None

        remaining_target_digit_inds = {t.digit: i for i, t in enumerate(tmp_target_words) if t is not None and t.digit is not None}
        remaining_response_digits = [r.digit for r in response_words if r is not None and r.digit is not None]

        #-- Loop through response, mark each said digit
        for i, resp in enumerate(remaining_response_digits):
            if resp in remaining_target_digit_inds:
                trg_ind = remaining_target_digit_inds[resp]
                del remaining_target_digit_inds[resp]
                target_digit_said[trg_ind] = True
                tmp_target_words[trg_ind] = None

        return target_word_said, target_digit_said

    #------------------------------------------------------
    def n_pairs(self, target, response, tind):
        """
        Find adjacent words with (class1, class2), and check if they appeared as an adjacent pair in the response

        Return:
        n_pairs_found - the number of pairs that existed in the target and the response
        n_mismatching_order - the number of pairs that existed in the target, and in the response existed with position violation (= not adjacent,
                              or not in the same order).

        :param target: list of words
        :param response: list of words
        :param tind: The index of the relevant target word
        """

        word1 = target[tind]
        word2 = target[tind+1]

        if word1 not in response or word2 not in response:
            return 0, 0

        correct_order_inds = [i for i, (r1, r2) in enumerate(zip(response[:-1], response[1:])) if r1 == word1 and r2 == word2]

        n_mismatching_order = int(len(correct_order_inds) == 0)

        return 1, n_mismatching_order

    #------------------------------------------------------
    def parse_response(self, response_str, rownum, target_segments):

        if response_str is None:
            return None

        if response_str in ('+', '!', 'v', 'V'):
            return target_segments

        if response_str in self.unknown_response_chars:
            return []

        response_str = str(response_str)

        #-- Check if there are optional things
        m = re.match('(.*);(.+)', response_str)
        if m is None:
            response_segments_unknown_loc = []
        else:
            response_str = m.group(1)
            response_segments_unknown_loc = self.parse_target_or_response(m.group(2), rownum)
            if response_segments_unknown_loc is None:
                return None

        try:
            response_segments = self.parse_target_or_response(response_str, rownum)
            if response_segments is None:
                return None
        except ValueError as e:
            print('Error in line {} (line ignored): {} '.format(rownum, e))
            return None

        target_has_duplicate_segments = len(target_segments) != len(set(target_segments))

        if len(response_segments) != len(target_segments) and ['correct'] in response_segments:
            print('WARNING: "+" is ambiguous because the target and response have different number of segments. ' +
                  'Line {} ignored'.format(rownum))
            return None

        #-- swap "+" with the corresponding target value
        for i, r in enumerate(response_segments):
            if list(r) == ['correct']:
                #-- double validation -- in case we have order mismatch. Validate this only if the tar
                if i >= len(target_segments) or (not target_has_duplicate_segments and target_segments[i] in response_segments):
                    print('WARNING: "+" is ambiguous because the target and response have different order of segments. ' +
                          'Line {} ignored'.format(rownum))
                    if self.fail_on_segment_order_error:
                        return None

                response_segments[i] = target_segments[i]

        return response_segments + response_segments_unknown_loc

    #------------------------------------------------------
    def collapse_segments(self, parsed_segments):
        return [x for pn in parsed_segments for x in pn]

    #------------------------------------------------------
    def parse_target_or_response(self, raw_text, rownum):
        """
        Return a list of segments, each of which is a list of words
        """

        if isinstance(raw_text, float):
            raw_text = "{:.0f}".format(raw_text)
        elif isinstance(raw_text, int):
            raw_text = "{:}".format(raw_text)

        segments = [e.strip() for e in raw_text.split('/')]

        parsed_segments = []
        for seg in segments:
            m = re.match('^([0-9,]*)\\s*t\\s*([0-9,]+)?$', seg)

            if m is None:
                parsed_segment = [self.parse_segment_into_word_list(seg)]
            else:
                parsed_segment = self._parse_pre_thousand_segment(m, seg)
                if m.group(2) is not None:
                    parsed_segment.append(self.parse_segment_into_word_list(m.group(2)))

            if None in parsed_segment:  # invalid format
                print('WARNING: unsupported target/response format: "{}" -- line {} ignored'.format(raw_text, rownum))
                return None

            #-- combine parts of the parsed segment
            parsed_segment = [e for seg in parsed_segment for e in seg]

            parsed_segments.append(tuple(parsed_segment))

        return parsed_segments

    #------------------------------------------------------
    def _parse_pre_thousand_segment(self, matcher, segment):
        """ Parse the 'thousand' and the preceding digits """

        if len(matcher.group(1)) == 0:
            # -- The word "thousand" with no preceding digit
            return [self.parse_segment_into_word_list('t')]

        elif len(matcher.group(1)) == 1:
            # -- A 4-digit number: the "thousand" is combined with the preceding digit
            return [self.parse_segment_into_word_list(matcher.group(1) + '000')]

        elif len(matcher.group(1)) in (2, 3):
            # -- A 5- or 6-digit number: the "thousand" is a separate word
            return [self.parse_segment_into_word_list(matcher.group(1)), self.parse_segment_into_word_list('t')]

        else:
            raise Exception('Unsupported format: {}'.format(segment))

    #------------------------------------------------------
    def parse_segment_into_word_list(self, segment):
        """
        Parse a number into a series of words

        :param segment: a string describing one grammatical segment (one number)
        """

        if segment in ('+', '!'):   # "+" is correct; "!" is TBD
            return ['correct']

        if segment in self.unknown_response_chars:
            return []

        #-- delete commas, set the unknown-digit characters to 'x'
        segment = segment.replace(',', '')
        for c in self.unknown_response_chars:
            if c not in ('x', 'X'):
                segment = segment.replace(c, 'x')

        result = verbalnumbers.hebrew.number_to_words(segment, digit_mapping=self._digit_mapping)

        if not self.consider_thousand_as_digit:
            one_thousand = verbalnumbers.general.NumberWord(verbalnumbers.hebrew.thousands, 1)
            decimal_word_thousand = verbalnumbers.general.NumberWord(verbalnumbers.hebrew.decword_thousand, None)
            for i, w in enumerate(result):
                if w == one_thousand:
                    result[i] = decimal_word_thousand

        return result

    #------------------------------------------------------
    def set_fixed_values(self, fixed_values, out_ws, out_row_num):
        for k, v in fixed_values.items():
            self._save_value(out_ws, out_row_num, k, v)

    #------------------------------------------------------
    def _open_worksheet(self, wb, worksheet, filename):
        if len(wb.worksheets) == 1:
            ws = wb.worksheets[0]
        else:
            sheet_names = [s.title for s in wb.worksheets]
            if worksheet not in sheet_names:
                raise ValueError('{} does not contain any worksheet named "{}"'.format(filename, worksheet))
            ws = wb.get_sheet_by_name(worksheet)

        col_inds = self._xls_structure(ws)

        return ws, col_inds

    def _xls_structure(self, ws):
        result = {}
        for i in range(1, ws.max_column+1):
            col_name = ws.cell(1, i).value
            result[col_name] = i

        missing_cols = [c for c in self.xls_cols if c not in result]
        if len(missing_cols) > 0:
            raise ValueError('Invalid file format: columns {} are missing'.format(",".join(missing_cols)))

        return result

    #------------------------------------------------------
    def create_output_workbook(self):

        wb = openpyxl.Workbook()
        ws = wb.worksheets[0]

        for i_col, colname in enumerate(self.xls_out_cols):
            ws.cell(1, i_col+1).value = colname

        return wb, ws

    #------------------------------------------------------
    def auto_col_width(self, ws):
        for col in ws.columns:
            max_length = 0
            for cell in col:
                try:  # Necessary to avoid error on empty cells
                    if len(str(cell.value)) > max_length:
                        max_length = len(cell.value)
                except:
                    pass

            ws.column_dimensions[col[0].column_letter].width = max_length

def _isnull(v):
    return v is None or v == '' or (isinstance(v, float) and math.isnan(v))


# 내 풀이

data = [input() for _ in range(int(input()))]

ans = list(data[0])           # 첫번째 파일만 따로 저장
for i in data[1:]:
    for j in range(len(i)):   # 그 다음 파일이랑 한글자씩 비교해서 다르면 '?'로 바꿔줌
        if ans[j] != i[j]:
            ans[j] = '?'

print(''.join(ans))           # 합쳐서 출력

'''
replace에 꽂혀서 그거로만 계속 하다가 틀렸다고 나와서 빡쳤었당
아무래도 replace는 같은 문자가 뒤에도 나오면 걔도 바뀌거나 아니면 바뀌는 횟수를 지정해줘야 돼서
이런 경우에는 적합하지 않은 듯
'''


#INGRESE 6 NOTAS Y MOSTRAR LA CANTIDAD DE APROBADAS(>=4 AL 7), PROMOCIONADOS(>=8) Y REPITENTES(<4)

from random import randint

repitentes=0
aprobados=0
promocionados=0

for i in range(1,7):
  print('Nota ' + str(i))
  nota=randint(1,10)
  print('Escriba la nota: ' + str(nota)) #SIN RANDINT nota=int(input('Escriba la nota: '))
  if 1<=nota<4:
    repitentes +=1
  elif 4<=nota<=7:
    aprobados+=1
  elif nota>=8:
    promocionados+=1
print('Reprobados = ' + str(repitentes))
print('Aprobados = ' + str(aprobados))
print('Promocionados = ' + str(promocionados))


"""Tests on Dockerfiles."""

from os import path
import typing
import unittest

_BLOCK_SEPARATOR = '\n\n'
# Prefix of blocks that are allowed to be only in Dockerfile.test.
_TEST_BLOCK_PREFIX = '# TEST ONLY.\n'

class SyncTestCase(unittest.TestCase):
    """Test that Dockerfile and Dockerfile.test are in sync.

    In order to run test as close as possible to the real environment we want
    to build almost the same Docker images, only adding what's needed for
    testing.
    """

    def test_sync(self) -> None:
        """Dockerfiles are in sync."""

        self.maxDiff = None  # pylint: disable=invalid-name
        dockerfile = [
            block.replace('COPY ', 'COPY frontend/')
            for block in _read_blocks('Dockerfile')
        ]
        dockerfile_test = [
            block for block in _read_blocks('Dockerfile.test')
            if not block.startswith(_TEST_BLOCK_PREFIX)
        ]
        self.assertEqual(
            _BLOCK_SEPARATOR.join(dockerfile),
            _BLOCK_SEPARATOR.join(dockerfile_test),
            msg='Keep Dockerfile and Dockerfile.test in sync.',
        )

def _read_blocks(filename: str) -> typing.List[str]:
    with open(path.join(path.dirname(path.dirname(__file__)), filename), 'r') as file_handle:
        file_content = file_handle.read()
    return [block.strip() for block in file_content.split(_BLOCK_SEPARATOR)]

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


gameHour = lambda x,  y: y - x if y > x else 24 + y - x
gameMinute = lambda xm, ym: (1, 60 + ym - xm) if ym < xm else ((0, ym - xm) if ym > xm else (0, xm - ym))
ah, am, bh, bm = map(int, input().split())
gHour = gameHour(ah , bh)
aMinute, bMinute = gameMinute(am, bm)
if gHour == 24 and aMinute == False:
    if gHour > 23 and bMinute > 0:
        gHour = 0
        print("O JOGO DUROU %i HORA(S) E %i MINUTO(S)" % (gHour, bMinute))
    else:
        print("O JOGO DUROU %i HORA(S) E %i MINUTO(S)" % (gHour, bMinute))
elif aMinute == True:
    gHour -= 1
    print("O JOGO DUROU %i HORA(S) E %i MINUTO(S)" % (gHour, bMinute))
else:
    print("O JOGO DUROU %i HORA(S) E %i MINUTO(S)" % (gHour, bMinute))


#!/usr/bin/env python3

import dbs,lines,spreadsheets,reports

inputFilename='../3-db.out/db.sql'

with dbs.Sqlite(inputFilename) as db:
	rows=lines.SectionRows()
	rowsData=rows.getData(db)
	cols=lines.AmendmentCols()
	colsData=cols.getData(db)


"""
Given an array of integers sorted in ascending order,
find the starting and ending position of a given target value.

Your algorithm's runtime complexity must be in the order of O(log n).

If the target is not found in the array, return [-1, -1].

For example,
Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].
"""

class Solution(object):
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        idx = self.search(nums, 0, len(nums)-1, target)
        if idx == -1:
            return [-1, -1]
        left = right = idx
        while left > 0 and nums[left-1] == nums[idx]:
            left -= 1
        while right < len(nums) - 1 and nums[right+1] == nums[idx]:
            right += 1
        return [left, right]

    def search(self, nums, left, right, target):
        if left > right:
            return -1
        mid = (left + right) / 2
        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            return self.search(nums, mid + 1, right, target)
        else:
            return self.search(nums, left, mid - 1, target)


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

# In[7]:

#1. Create a function that transposes a 2D matrix.

def transpose_matrix(l):
    l2=[]
    for i in range (len(l[0])):
        l1=[]
        for j in range(len(l)):
            l1.append(l[j][i])
        l2.append(l1)
    return(l2)

# In[8]:

transpose_matrix([
  [5, 5],
  [6, 7],
  [9, 1]
])

#2. Create a function that determines whether a string is a valid hex code.
# A hex code must begin with a pound key # and is exactly 6 characters in length. Each character must be a digit from 0-9 or an alphabetic character from A-F. All alphabetic characters may be uppercase or lowercase.
# In[ ]:

def is_valid_hex_code(s):
    if len(s)==7 and s[0]=="#":
        for letter in s[1:]:
            if letter.lower() not in "abcdef" and  not letter.isdigit():
                return False
            else:
                return True

    else:
        return False

# In[26]:

is_valid_hex_code("#CD5C5C")

# In[27]:

is_valid_hex_code("#EAECEE")

# In[28]:

is_valid_hex_code("#CD5C58C")

#3. Given a list of math equations (given as strings), return the percentage of correct answers as a string. Round to #the nearest whole number.
# In[ ]:

def mark_maths(l):
    count=0
    for i in l:
        l1=i.split("=")
        if(eval(l1[0])==int(l1[1])):
            count+=1
    return str(round((count/len(l))*100))+"%"

# In[42]:

mark_maths(["2+2=4", "3+2=5", "10-3=3", "5+5=10"])

# In[39]:

# In[38]:

mark_maths(["2+2=4", "3+2=5", "10-3=3", "5+5=10"])

# 5. From point A, an object is moving towards point B at constant velocity va (in km/hr). From point B, another object is moving towards point A at constant velocity vb (in km/hr). Knowing this and the distance between point A and B (in km), write a function that returns how much time passes until both objects meet.
# In[ ]:

def lets_meet(dis,vel1,vel2):
    time=(dis/(vel1+vel2))*60
    hours, seconds=divmod(time*60, 3600)
    minutes, seconds=divmod(seconds, 60)
    return str(round(hours))+"h "+str(round(minutes))+"min "+str(round(seconds))+"sec"

# In[58]:

lets_meet(100, 10, 30)

# In[59]:

lets_meet(280, 70, 80)

# In[ ]:


import pandas as pd
import numpy as np
from datetime import datetime

df = pd.DataFrame.from_dict(weather['forecasts'][0], orient='index').transpose()
for forecast in weather['forecasts'][1:]:
    df = pd.concat([df, pd.DataFrame.from_dict(forecast, orient='index').transpose()])

# extract time and use it as index
time = np.array(df['fcst_valid_local'])
for row in range(len(time)):
    time[row] = datetime.strptime(time[row], '%Y-%m-%dT%H:%M:%S+0100')

df = df.set_index(time)

list(df)


def fibonacy(num):
    a=0
    b=1
    if num==1:
        print(a)
    elif num<0:
        print("INVALID")
    else:
        print(a)
        print(b)
    for i in range(2,num):
        c=a+b
        a=b
        b=c
        print(c)
number=int(input("Enter the nuber whose fibonacy you want to calculate:"))
fibonacy(number)


"""
This module defines all the command line interface arguments that can be used to call the CLI.
"""

from argparse import ArgumentParser

parser = ArgumentParser(
    prog='kanbanpy', description='Console-based kanban task manager')
parser.add_argument('-v', '--version', action='store_true', help='display program version')

subparsers = parser.add_subparsers(dest='command')

create_parser = subparsers.add_parser(
    'create', help='create a new task', description='Create a new task',
    aliases=['c'], epilog='Include a "!" in the name to mark the task as important.')
create_parser.add_argument('title', type=str, help='task title')

next_parser = subparsers.add_parser(
    'next', help='move a task to the next status', description='Move a task to the next status', aliases=['n'])
next_parser.add_argument('id', type=int, help='task id')

prev_parser = subparsers.add_parser('prev', help='move a task to the previous status',
                                    description='Move a task to the previous status', aliases=['p'])
prev_parser.add_argument('id', type=int, help='task id')

remove_parser = subparsers.add_parser(
    'remove', help='remove a task', description='Remove a task', aliases=['r'])
remove_parser.add_argument('id', type=int, help='task id')

clear_parser = subparsers.add_parser(
    'clear', help='remove all completed tasks', description='Remove all completed tasks')

setup_parser = subparsers.add_parser(
    'setup', help='create the configuration file', description='Create the configuration file')
setup_parser.add_argument('-y', dest='defaults',
                          action='store_true', help='use defaults')

backup_parser = subparsers.add_parser(
    'backup', help='create a backup file', description='Create a backup file')
backup_parser.add_argument(
    '-o', dest='output', type=str, help='destination file')


import os
from google_service import create_service
from googleapiclient.http import MediaFileUpload
import magic
from icecream import ic

def get_mime_type(fl_nm):
    """
    :param fl_nm: Path to a file
    """
    return magic.Magic(mime=True).from_file(fl_nm)

class GoogleDriveUploader:
    """
    Upload a file to Google Drive & get publicly sharable URL
    """

    API_NAME = 'drive'
    API_VERSION = 'v3'
    SCOPE = 'https://www.googleapis.com/auth/drive'

    def __init__(self, client_secret_file):
        self.service = create_service(
            client_secret_file, self.API_NAME, self.API_VERSION, [self.SCOPE])
        # ic(dir(self.service))
        self.folders = self.get_folders()

    def get_folders(self):
        res = self.service.files().list(
            q="mimeType = 'application/vnd.google-apps.folder'",
            spaces='drive',
            fields='nextPageToken, files(id, name)',
            pageToken=None
        ).execute()
        return res.get('files', [])

    def get_folder_id(self, fd_nm):
        return next(filter(lambda x: x['name'] == fd_nm, self.folders))['id']

    def upload_files(self, fl_nms, fd_nm):
        """
        :param fl_nms: List of local file paths to upload
        :param fd_nm: The folder name in Google Drive to upload to
        :return: List of uploaded file ids
        """
        fd_id = self.get_folder_id(fd_nm)

        def upload_n_get_id(fl_nm):
            fl_nm_ = os.path.basename(fl_nm)
            res = self.service.files().create(
                body=dict(
                    name=fl_nm_,
                    parents=[fd_id]
                ),
                media_body=MediaFileUpload(fl_nm, mimetype=get_mime_type(fl_nm)),
                fields='id'
            ).execute()
            return res['id']
        return list(map(upload_n_get_id, fl_nms))

    def get_shareable_url(self, fl_ids, verbose=False):
        """
        :param fl_ids: List of file ids
        :param verbose: Print status
        :return: List of urls

        .. note:: File permissions are set to public
        """
        def _get(fl_id):
            res = self.service.permissions().create(
                fileId=fl_id,
                body=dict(
                    role='reader',
                    type='anyone'
                )
            ).execute()

            k = 'webViewLink'
            res = self.service.files().get(
                fileId=fl_id,
                fields=k
            ).execute()

            if verbose:
                ic('Changed file permission for', res)
            return res[k]
        return list(map(_get, fl_ids))

if __name__ == '__main__':
    os.chdir('..')
    FDNM = 'StefanHeng.github.io'
    CLIENT_SECRET_FILE = 'scripts/client-secret.json'

    gdu = GoogleDriveUploader(CLIENT_SECRET_FILE)
    ic(gdu.folders[:5])
    ic(gdu.get_folder_id(FDNM))

    ids = gdu.upload_files(['upload-resources/CV 2.4, Stefan Heng, 12.06.21.pdf'], FDNM)
    ic(gdu.get_shareable_url(ids, verbose=True))


# https://leetcode.com/problems/combinations/
# MEDIUM
# backtracklc, #77

# GIVEN:
    # 2 integers, n and k

# TASK:
    # return all possible combinations of k numbers chosen from the range [1, n]

# EXAMPLES:
    # Input: n = 4, k = 2
    # Output: [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
    # Explanation: There are 4 choose 2 = 6 total combinations.
    # Note that combinations are unordered, i.e., [1,2] and [2,1] are considered to be the same combination.

    # Input: n = 1, k = 1
    # Output: [[1]]
    # Explanation: There is 1 choose 1 = 1 total combination.

###########################################################################################################

# ✅ ALGORITHM 1: RECURSIVE BACKTRACKING

# TIME COMPLEXITY: O(nk)
    # backtrack function is called n times, because there are n possible starting points for the subset
    # For each starting point, the backtrack function iterates through all k elements
        # This is because the comb list must contain all k elements to be a valid subset
# SPACE COMPLEXITY: O(k)
    # The comb list stores at most k elements

def combine(n, k):
    result = []

    # when doing recursive backtracking, we need to remember which value we started at so we only choose values after it
    # we need current combinations as, when we go down decision tree, we need to know what previous values we added

    def backtrack(start, curr_combination):
        # start is the value we start making combinations from
            # e.g. if start = 2, combinations = [2, 3], [2, 4], etc.
        if len(curr_combination) == k: # we stop the backtracking when we have k combinations – we don't add anymore elements to it
            result.append(curr_combination[:]) # since we will continue to add to curr_combination in other recursive calls, we add a copy of it to result so when we update combinations we don't add it to result
            return

        for i in range(start, n+1): # i = 1 to n
            curr_combination.append(i)
            backtrack(i+1, curr_combination)
            curr_combination.pop() # after choosing i, now we want to choose the next value -> clean up the i before we add the next one

    backtrack(1, []) # we start at 1 since the values we can form combinations with are 1-n
    return result


# 현재 설치된 구조물이 '가능한' 구조물인지 확인하는 함수
def possible(answer):
    for x, y, stuff in answer:
        if stuff == 0: # 설치된 것이 '기둥'인 경우
            # '바닥 위' 혹은 '보의 한쪽 끝 부분 위' 혹은 '다른 기둥 위'라면 정상
            if y == 0 or [x - 1, y, 1] in answer or [x, y, 1] in answer or [x, y - 1, 0] in answer:
                continue
            return False # 아니라면 거짓(False) 반환
        elif stuff == 1: # 설치된 것이 '보'인 경우
            # '한쪽 끝부분이 기둥 위' 혹은 '양쪽 끝부분이 다른 보와 동시에 연결'이라면 정상
            if [x, y - 1, 0] in answer or [x + 1, y - 1, 0] in answer or ([x - 1, y, 1] in answer and [x + 1, y, 1] in answer):
                continue
            return False # 아니라면 거짓(False) 반환
    return True

def solution(n, build_frame):
    answer = []
    for frame in build_frame: # 작업(frame)의 개수는 최대 1,000개
        x, y, stuff, operate = frame
        if operate == 0: # 삭제하는 경우
            answer.remove([x, y, stuff]) # 일단 삭제를 해본 뒤에
            if not possible(answer): # 가능한 구조물인지 확인
                answer.append([x, y, stuff]) # 가능한 구조물이 아니라면 다시 설치
        if operate == 1: # 설치하는 경우
            answer.append([x, y, stuff]) # 일단 설치를 해본 뒤에
            if not possible(answer): # 가능한 구조물인지 확인
                answer.remove([x, y, stuff]) # 가능한 구조물이 아니라면 다시 제거
    return sorted(answer) # 정렬된 결과를 반환


#!/bin/python3

import sys

w = input().strip()
flag = 0

for i in range(len(w)-1):
    if w[i] == w[i+1] or (w[i] in ['a', 'e', 'i', 'o', 'u', 'y'] and w[i+1] in ['a', 'e', 'i', 'o', 'u', 'y']):
        flag = 1
        break

if flag == 1:
    print('No')
else:
    print('Yes')

# Print 'Yes' if the word is beautiful or 'No' if it is not.


from django.shortcuts import render
from django.http import HttpResponse
from .forms import Ceaser
# Create your views here.

def index(request):
	form = Ceaser(request.POST or None)
	text = ''
	key = 0
	context = {
		'form':form
	}
	if form.is_valid():
		key = form.cleaned_data.get('key')
		text = form.cleaned_data.get('user_text')
		result = ceaser_cipher(text, key)
		context = {'form':form,'result':result}
	return render(request, 'index.html', context)

def ceaser_cipher(plaintext,key):
	alphabet = "abcdefghijklmnopqrstuvwxyz"

	cipher = ''
	for c in plaintext:
		if c.isalpha():
			cipher += alphabet[alphabet.index(c) +key]
		else:
			cipher+= c
	return cipher


import cv2
import numpy as np
import matplotlib.pyplot as plt

# %%
img = cv2.imread('./data/image_3.jpg', 0)
print(img.shape)
# %%

def bgr2rgb(img):
    """
    The cv2 reads image in BGR format while plt showes image in RGB format.
    To overcome this follow:
    b,g,r = cv2.split(img)
    img = cv2.merge([r,g,b])
    """
    if len(img.shape) == 3 and img.shape[2] == 3:
        b, g, r = cv2.split(img)
        out = cv2.merge([r, g, b])
    else:
        out = img
    return out

# %% Histogram eqalization
hist, bins = np.histogram(img.flatten(), 256, (0, 256))
cdf = hist.cumsum()
cdf_normalized = cdf * hist.max()/hist.cumsum().max()
# Histogram Equalization
equ = cv2.equalizeHist(img)
hist_equ, bins_equ = np.histogram(equ.flatten(), 256, (0, 256))
# Contrast Limited Adaptive Histogram Equalization
clahe = cv2.createCLAHE(clipLimit=20.0, tileGridSize=(8, 8))
cl1 = clahe.apply(img)
hist_cl1, bins_cl1 = np.histogram(cl1.flatten(), 256, (0, 256))
# Horizontal stack of images
res = np.hstack((img, equ, cl1))

plt.figure(1)
plt.imshow(bgr2rgb(res), cmap='gray')
# %%
plt.figure(2)
plt.plot(hist, color='b', label="hist")
plt.plot(hist_equ, color='g', label="hist equalized")
plt.plot(hist_cl1, color='r', label="hist CLAHE")
plt.plot(cdf_normalized, 'y', label='CDF normalized')
plt.legend()
plt.show()


from kerastuner import HyperModel
from kerastuner.tuners import Hyperband
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import (Dense,
                                     Flatten,
                                     Conv2D,
                                     MaxPooling2D,
                                     BatchNormalization,
                                     Dropout)
from tensorflow import keras
from tensorflow.keras.utils import to_categorical
from tensorflow.keras import backend as K
import numpy as np

# import Keras Tuner Hypermodel Class
class CNNHyperModel(HyperModel):
    def __init__(self, input_shape, num_classes):
        self.input_shape = input_shape
        self.num_classes = num_classes

    def build(self, hp):
        model = keras.Sequential()
        model.add(
            Conv2D(
                filters=256,
                kernel_size=(3),
                strides=(2, 2),
                activation='relu',
            )
        )
        model.add(
            Conv2D(
                filters=hp.Choice(
                    'num_filters_1',
                    values=[32, 64],
                    default=32,
                ),
                activation='relu',
                kernel_size=3
            )
        )
        model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))
        model.add(BatchNormalization())
        model.add(
            Dropout(rate=hp.Float(
                'dropout_1',
                min_value=0.0,
                max_value=0.5,
                default=0.25,
                step=0.05,
            ))
        )
        model.add(
            Conv2D(
                filters=16,
                kernel_size=3,
                activation='relu',
            )
        )
        model.add(Flatten())
        model.add(
            Dropout(rate=hp.Float(
                'dropout_2',
                min_value=0.0,
                max_value=0.5,
                default=0.25,
                step=0.05,
            ))
        )
        model.add(BatchNormalization())
        model.add(Dense(self.num_classes, activation='softmax'))

        model.compile(optimizer='Adam',
                      loss='categorical_crossentropy',
                      metrics=['accuracy']
                      )
        return model

(X_train, y_train), (X_test, y_test) = mnist.load_data()

X_train_reshaped = np.expand_dims(X_train.astype('float32'), axis=3)
X_test_reshaped = np.expand_dims(X_test.astype('float32'), axis=3)
y_train_reshaped = to_categorical(y_train.astype('float32'))
y_test_reshaped = to_categorical(y_test.astype('float32'))

K.clear_session()

INPUT_SHAPE = (28, 28, 1)
NUM_CLASSES = 10

# Hypermodel definition
hypermodel = CNNHyperModel(input_shape=INPUT_SHAPE, num_classes=NUM_CLASSES)

# Define tuner
HYPERBAND_MAX_EPOCHS = 40
MAX_TRIALS = 20
SEED = 1
EXECUTION_PER_TRIAL = 2

tuner = Hyperband(
    hypermodel,
    max_epochs=HYPERBAND_MAX_EPOCHS,
    objective='val_accuracy',
    seed=SEED,
    executions_per_trial=EXECUTION_PER_TRIAL,
    directory='hyperband',
    project_name='MNIST'
)

# Search space summary of tuner
tuner.search_space_summary()

# Start the tuning
N_EPOCH_SEARCH = 40
tuner.search(
        X_train_reshaped,
        y_train_reshaped,
        epochs=N_EPOCH_SEARCH,
        validation_split=0.1
        )

# Summary of the results
tuner.results_summary()

# Retrieve best model
best_model = tuner.get_best_models(num_models=1)[0]

# Evaluate best model
loss, accuracy = best_model.evaluate(X_test_reshaped, y_test_reshaped)

print(f"""
The hyperparameter search is complete. The optimal number of filters
in the first convoluted layer is
{best_model.get('num_filters_1')},
the optimal dropout rate for the first dropout layer is
{best_model.get('dropout_1')},
and the optimal dropout rate for the second dropout layer is
{best_model.get('dropout_2')}.
""")

print(f'Loss rate on best model: {loss}')
print(f'Accuracy on best model: {accuracy}')


t = 'CADRASTRO DE PESSOAS (BREAK)'
print(f'{t:=^105}')

from datetime import date
atual = date.today().year
#print(atual)

i = 's'
s = 's'
continuar = ' '
m = 0
f = 0
p = 0
while True:
    print('-' * 25)
    print(' CADASTRE UMA PESSOA')
    print('-' * 25)
    i = int(input('Idade: '))
    s = continuar = ' '
    while s not in 'MmFf':
        s = str(input('Sexo[M/F]: ')).upper().strip()[0]
    while continuar not in 'SsNn':
        continuar = str(input('Quer Continuar [S/N]?')).upper().strip()
    if s == 'M':
        m += 1
    if s == 'F' and i <= 20:
        f += 1
    if i >= 18:
        p += 1
    if continuar == 'N':
        break
print(f'''Total de pessoas com mais de 18 anos: {p}
Ao todo temos {m} homens cadastrados cadastrados
E temos {f} mulheres com menos de 20 anos''')


from datetime import timedelta
from typing import Optional
from uuid import uuid4

from django.conf import settings
from django.core.mail import send_mail
from django.db import models, transaction
from django.db.models import SET_NULL, Q
from django.db.utils import IntegrityError
from django.dispatch import Signal
from django.http import HttpRequest
from django.urls import reverse
from django.utils.crypto import get_random_string
from django.utils.timezone import now

from phonenumber_field.modelfields import PhoneNumberField

from aidants_connect_common.utils.constants import (
    MessageStakeholders,
    RequestOriginConstants,
    RequestStatusConstants,
)
from aidants_connect_common.utils.email import render_email
from aidants_connect_common.utils.urls import build_url
from aidants_connect_web.models import (
    Aidant,
    HabilitationRequest,
    Organisation,
    OrganisationType,
)
from aidants_connect_web.utilities import generate_new_datapass_id

__all__ = [
    "PersonWithResponsibilities",
    "Issuer",
    "IssuerEmailConfirmation",
    "Manager",
    "OrganisationRequest",
    "AidantRequest",
    "RequestMessage",
    "email_confirmation_sent",
]

def _new_uuid():
    return uuid4()

class PersonEmailField(models.EmailField):
    def __init__(self, **kwargs):
        kwargs["max_length"] = 150
        kwargs["verbose_name"] = "Email nominatif"
        super().__init__(**kwargs)

class Person(models.Model):
    first_name = models.CharField("Prénom", max_length=150)
    last_name = models.CharField("Nom", max_length=150)
    email = PersonEmailField()
    profession = models.CharField("Profession", blank=False, max_length=150)

    def __str__(self):
        return f"{self.first_name} {self.last_name}"

    def save(
        self, force_insert=False, force_update=False, using=None, update_fields=None
    ):
        if self.email:
            self.email = self.email.lower()
        if update_fields is not None:
            update_fields = {"email"}.union(update_fields)
        super().save(force_insert, force_update, using, update_fields)

    def get_full_name(self):
        return str(self)

    class Meta:
        abstract = True

class PersonWithResponsibilities(Person):
    phone = PhoneNumberField("Téléphone", blank=True)

    class Meta:
        abstract = True

class Issuer(PersonWithResponsibilities):
    email = PersonEmailField(unique=True)
    issuer_id = models.UUIDField(
        "Identifiant de demandeur", default=_new_uuid, unique=True
    )

    email_verified = models.BooleanField(verbose_name="Email vérifié", default=False)

    def get_absolute_url(self):
        return reverse(
            "habilitation_issuer_page",
            kwargs={"issuer_id": self.issuer_id},
        )

    class Meta:
        verbose_name = "Demandeur"

class EmailConfirmationManager(models.Manager):
    def all_expired(self):
        return self.filter(self.expired_q())

    def all_valid(self):
        return self.exclude(self.expired_q())

    def expired_q(self):
        sent_threshold = now() - timedelta(days=settings.EMAIL_CONFIRMATION_EXPIRE_DAYS)
        return Q(sent__lt=sent_threshold)

    def delete_expired_confirmations(self):
        self.all_expired().delete()

email_confirmation_sent = Signal()

def _get_default_email_key():
    return get_random_string(64).lower()

class IssuerEmailConfirmation(models.Model):
    issuer = models.ForeignKey(
        Issuer, on_delete=models.CASCADE, related_name="email_confirmations"
    )
    created = models.DateTimeField(verbose_name="Créé le", default=now)
    sent = models.DateTimeField(verbose_name="Envoyée", null=True)
    key = models.CharField(
        verbose_name="Clé", max_length=64, unique=True, default=_get_default_email_key
    )

    objects = EmailConfirmationManager()

    @property
    def key_expired(self) -> bool:
        expiration_date = self.sent + timedelta(
            days=settings.EMAIL_CONFIRMATION_EXPIRE_DAYS
        )
        return expiration_date <= now()

    class Meta:
        verbose_name = "Confirmation d'email"
        verbose_name_plural = "Confirmations d'email"

    def __str__(self):
        return "Confirmation pour %s" % self.issuer.email

    @classmethod
    def for_issuer(cls, issuer) -> "IssuerEmailConfirmation":
        return cls._default_manager.create(issuer=issuer)

    def confirm(self) -> Optional[str]:
        if self.issuer.email_verified:
            return self.issuer.email

        if self.key_expired:
            return None

        self.issuer.email_verified = True
        self.issuer.save()

        return self.issuer.email

    def send(self, request: HttpRequest):
        self.sent = now()
        self.save()
        email_confirmation_sent.send(self.__class__, request=request, confirmation=self)

class Manager(PersonWithResponsibilities):
    address = models.TextField("Adresse")
    zipcode = models.CharField("Code Postal", max_length=10)
    city = models.CharField("Ville", max_length=255)

    city_insee_code = models.CharField(
        "Code INSEE de la ville", max_length=5, null=True, blank=True
    )

    department_insee_code = models.CharField(
        "Code INSEE du département", max_length=5, null=True, blank=True
    )

    is_aidant = models.BooleanField("C'est aussi un aidant", default=False)

    class Meta:
        verbose_name = "Référent structure"
        verbose_name_plural = "Référents structure"

class OrganisationRequest(models.Model):
    created_at = models.DateTimeField("Date création", auto_now_add=True)

    updated_at = models.DateTimeField("Date modification", auto_now=True)

    organisation = models.ForeignKey(
        Organisation,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )

    issuer = models.ForeignKey(
        Issuer,
        on_delete=models.CASCADE,
        related_name="organisation_requests",
        verbose_name="Demandeur",
    )

    manager = models.OneToOneField(
        Manager,
        on_delete=models.CASCADE,
        related_name="organisation",
        verbose_name="Référent",
        default=None,
        null=True,
    )

    uuid = models.UUIDField(
        "Identifiant de brouillon",
        default=_new_uuid,
        unique=True,
    )

    data_pass_id = models.IntegerField(
        "Numéro Datapass",
        null=True,
        default=None,
        unique=True,
    )

    status = models.CharField(
        "État",
        max_length=150,
        default=RequestStatusConstants.NEW.name,
        choices=RequestStatusConstants.choices,
    )

    type = models.ForeignKey(OrganisationType, null=True, on_delete=SET_NULL)

    type_other = models.CharField(
        "Type de structure si autre",
        blank=True,
        default="",
        max_length=200,
    )

    # Organisation
    name = models.TextField("Nom de la structure")
    siret = models.BigIntegerField("N° SIRET")
    address = models.TextField("Adresse")
    zipcode = models.CharField("Code Postal", max_length=10)
    city = models.CharField("Ville", max_length=255, blank=True)

    city_insee_code = models.CharField(
        "Code INSEE de la ville", max_length=5, null=True, blank=True
    )

    department_insee_code = models.CharField(
        "Code INSEE du département", max_length=5, null=True, blank=True
    )

    is_private_org = models.BooleanField("Structure privée", default=False)
    partner_administration = models.CharField(
        "Administration partenaire",
        blank=True,
        default="",
        max_length=200,
    )
    public_service_delegation_attestation = models.FileField(
        "Attestation de délégation de service public",
        blank=True,
        default="",
    )

    france_services_label = models.BooleanField(
        "Labellisation France Services", default=False
    )
    france_services_number = models.CharField(
        "Numéro d’immatriculation France Services",
        blank=True,
        default="",
        max_length=200,
    )

    web_site = models.URLField("Site web", blank=True, default="")

    mission_description = models.TextField("Description des missions de la structure")

    avg_nb_demarches = models.IntegerField(
        "Nombre moyen de démarches ou de dossiers traités par semaine"
    )

    # Checkboxes
    cgu = models.BooleanField("J'accepte les CGU", default=False)
    dpo = models.BooleanField("Le DPO est informé", default=False)
    professionals_only = models.BooleanField(
        "La structure ne contient que des aidants professionnels", default=False
    )
    without_elected = models.BooleanField(
        "Aucun élu n'est impliqué dans la structure", default=False
    )

    @property
    def is_draft(self):
        return RequestStatusConstants[self.status] == RequestStatusConstants.NEW

    @property
    def status_label(self):
        return RequestStatusConstants[self.status].label

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse(
            "habilitation_organisation_view",
            kwargs={"issuer_id": self.issuer.issuer_id, "uuid": self.uuid},
        )

    def notify_issuer_request_submitted(self):
        text_message, html_message = render_email(
            "email/organisation_request_creation.mjml",
            {
                "url": build_url(self.get_absolute_url()),
                "organisation": self,
            },
        )

        send_mail(
            from_email=settings.EMAIL_ORGANISATION_REQUEST_FROM,
            recipient_list=[self.issuer.email],
            subject=settings.EMAIL_ORGANISATION_REQUEST_SUBMISSION_SUBJECT,
            message=text_message,
            html_message=html_message,
        )

    def notify_issuer_request_modified(self):
        text_message, html_message = render_email(
            "email/organisation_request_modifications_done.mjml",
            {
                "url": build_url(self.get_absolute_url()),
                "organisation": self,
            },
        )

        send_mail(
            from_email=settings.EMAIL_ORGANISATION_REQUEST_FROM,
            recipient_list=[self.issuer.email],
            subject=settings.EMAIL_ORGANISATION_REQUEST_MODIFICATION_SUBJECT,
            message=text_message,
            html_message=html_message,
        )

    def prepare_request_for_ac_validation(self, form_data: dict):
        self.cgu = form_data["cgu"]
        self.dpo = form_data["dpo"]
        self.professionals_only = form_data["professionals_only"]
        self.without_elected = form_data["without_elected"]
        if self.status == RequestStatusConstants.NEW.name:
            self.status = RequestStatusConstants.AC_VALIDATION_PROCESSING.name
            self.data_pass_id = int(f"{self.zipcode[:3]}{generate_new_datapass_id()}")
            self.save()
            self.notify_issuer_request_submitted()
        if self.status == RequestStatusConstants.CHANGES_REQUIRED.name:
            self.status = RequestStatusConstants.AC_VALIDATION_PROCESSING.name
            self.save()
            self.notify_issuer_request_modified()

    @transaction.atomic
    def go_in_waiting_again(self):
        self.status = RequestStatusConstants.AC_VALIDATION_PROCESSING.name
        self.save()

    @transaction.atomic
    def accept_request_and_create_organisation(self):
        if self.status != RequestStatusConstants.AC_VALIDATION_PROCESSING.name:
            return False

        try:
            organisation_type, _ = OrganisationType.objects.get_or_create(
                name=(self.type_other if self.type_other else self.type)
            )
            organisation = Organisation.objects.create(
                name=self.name,
                type=organisation_type,
                siret=self.siret,
                address=self.address,
                zipcode=self.zipcode,
                city=self.city,
                city_insee_code=self.city_insee_code,
                department_insee_code=self.department_insee_code,
                data_pass_id=self.data_pass_id,
            )
        except IntegrityError:
            raise Organisation.AlreadyExists(
                "Il existe déjà une organisation portant le n° datapass "
                f"{self.data_pass_id}."
            )

        self.organisation = organisation
        self.status = RequestStatusConstants.VALIDATED.name
        self.save()

        responsable_query = Aidant.objects.filter(username__iexact=self.manager.email)

        if not responsable_query.exists():
            responsable = Aidant.objects.create(
                first_name=self.manager.first_name,
                last_name=self.manager.last_name,
                email=self.manager.email,
                username=self.manager.email,
                phone=self.manager.phone,
                profession=self.manager.profession,
                organisation=organisation,
                can_create_mandats=False,
            )

            responsable.responsable_de.add(organisation)
            responsable.save()
        else:
            responsable = responsable_query[0]
            responsable.responsable_de.add(organisation)
            if responsable.has_a_totp_device:
                self.status = RequestStatusConstants.CLOSED.name
                self.save()
            responsable.save()

        self.create_aidants(organisation)

        if self.manager.is_aidant:
            HabilitationRequest.objects.get_or_create(
                email=self.manager.email,
                organisation=organisation,
                defaults=dict(
                    origin=HabilitationRequest.ORIGIN_HABILITATION,
                    first_name=self.manager.first_name,
                    last_name=self.manager.last_name,
                    profession=self.manager.profession,
                ),
            )

        return True

    @transaction.atomic
    def create_aidants(self, organisation: Organisation):
        for aidant in self.aidant_requests.all():
            HabilitationRequest.objects.get_or_create(
                email=aidant.email,
                organisation=organisation,
                defaults=dict(
                    origin=HabilitationRequest.ORIGIN_HABILITATION,
                    first_name=aidant.first_name,
                    last_name=aidant.last_name,
                    profession=aidant.profession,
                ),
            )

    def refuse_request(self):
        if self.status != RequestStatusConstants.AC_VALIDATION_PROCESSING.name:
            return False
        self.status = RequestStatusConstants.REFUSED.name
        self.save()
        return True

    def require_changes_request(self):
        if self.status != RequestStatusConstants.AC_VALIDATION_PROCESSING.name:
            return False
        self.status = RequestStatusConstants.CHANGES_REQUIRED.name
        self.save()
        return True

    class Meta:
        constraints = [
            models.CheckConstraint(
                check=(
                    (
                        ~Q(type_id=RequestOriginConstants.OTHER.value)
                        & Q(type_other__isnull_or_blank=True)
                    )
                    | (
                        Q(type_id=RequestOriginConstants.OTHER.value)
                        & Q(type_other__isnull_or_blank=False)
                    )
                ),
                name="type_other_correctly_set",
            ),
            models.CheckConstraint(
                check=(
                    (
                        Q(is_private_org=True)
                        & Q(partner_administration__isnull_or_blank=False)
                    )
                    | (
                        Q(is_private_org=False)
                        & Q(partner_administration__isnull_or_blank=True)
                    )
                ),
                name="partner_administration_if_org_is_private",
            ),
            models.CheckConstraint(
                check=(
                    (
                        Q(france_services_label=True)
                        & Q(france_services_number__isnull_or_blank=False)
                    )
                    | (
                        Q(france_services_label=False)
                        & Q(france_services_number__isnull_or_blank=True)
                    )
                ),
                name="immatriculation_number_if_france_services_label",
            ),
            models.CheckConstraint(
                check=Q(status=RequestStatusConstants.NEW.name)
                | (~Q(status=RequestStatusConstants.NEW.name) & Q(cgu=True)),
                name="cgu_checked",
            ),
            models.CheckConstraint(
                check=Q(status=RequestStatusConstants.NEW.name)
                | (~Q(status=RequestStatusConstants.NEW.name) & Q(dpo=True)),
                name="dpo_checked",
            ),
            models.CheckConstraint(
                check=Q(status=RequestStatusConstants.NEW.name)
                | (
                    ~Q(status=RequestStatusConstants.NEW.name)
                    & Q(professionals_only=True)
                ),
                name="professionals_only_checked",
            ),
            models.CheckConstraint(
                check=Q(status=RequestStatusConstants.NEW.name)
                | (
                    ~Q(status=RequestStatusConstants.NEW.name) & Q(without_elected=True)
                ),
                name="without_elected_checked",
            ),
            models.CheckConstraint(
                check=Q(status=RequestStatusConstants.NEW.name)
                | (
                    ~Q(status=RequestStatusConstants.NEW.name)
                    & Q(manager__isnull=False)
                ),
                name="manager_set",
            ),
        ]
        verbose_name = "Demande d’habilitation"
        verbose_name_plural = "Demandes d’habilitation"

class AidantRequest(Person):
    organisation = models.ForeignKey(
        OrganisationRequest,
        on_delete=models.CASCADE,
        related_name="aidant_requests",
    )

    @property
    def is_draft(self):
        return self.organisation.is_draft

    @property
    def uuid(self):
        return self.organisation.uuid

    class Meta:
        verbose_name = "aidant à habiliter"
        verbose_name_plural = "aidants à habiliter"
        unique_together = (("email", "organisation"),)

class RequestMessage(models.Model):
    organisation = models.ForeignKey(
        OrganisationRequest,
        on_delete=models.CASCADE,
        related_name="messages",
    )

    sender = models.CharField(
        "Expéditeur", max_length=20, choices=MessageStakeholders.choices
    )
    content = models.TextField("Message")

    created_at = models.DateTimeField(auto_now_add=True)

    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"Message {self.id}"

    class Meta:
        verbose_name = "message"
        ordering = ("created_at",)

    def send_message_email(self):
        text_message, html_message = render_email(
            "email/new_message_received.mjml",
            {
                "url": build_url(self.organisation.get_absolute_url()),
                "organisation": self.organisation,
                "message": self,
            },
        )

        send_mail(
            from_email=settings.EMAIL_ORGANISATION_REQUEST_FROM,
            recipient_list=[self.organisation.issuer.email],
            subject=settings.EMAIL_NEW_MESSAGE_RECEIVED_SUBJECT,
            message=text_message,
            html_message=html_message,
        )

    def save(self, *args, **kwargs):
        if not self.pk and self.sender == "AC":
            self.send_message_email()
        return super(RequestMessage, self).save(*args, **kwargs)

def _default_expiraton_date():
    return now() + timedelta(days=1)

class AddressAPIResultQuerySet(models.QuerySet):
    def expired(self):
        return self.filter(expiraton_date__lt=now())


from mrjob.job import MRJob
from mrjob.step import MRStep
from mrjob.protocol import JSONValueProtocol
import re
import itertools
import json
import time

start_time = time.time()

class JSONProtocol(object):

    def read(self, line):
        k_str, v_str = line.split('\t', 1)
        return json.loads(k_str), json.loads(v_str)

    def write(self, key, value):
        return '%s,%s' % (key, value)

class UsersCount(MRJob):

    #OUTPUT_PROTOCOL = JSONProtocol

    def mapper_userid(self, _, line):
        json_data = json.loads(line)
        if len(json_data) == 15:
            yield json_data["business_id"], ["categories_tag", json_data["name"], json_data["categories"]]
        else:
            yield json_data["business_id"], [json_data["stars"]]

    def reducer_1(self, business_id, data):
        data_list = [d for d in data]
        categories_list = []
        stars = []
        if len(data_list) > 1:
            for data in data_list:
                if data[0] == "categories_tag":
                    categories_list = data[2]
                else:
                    stars.append(data)
            for category in categories_list:
                for star in stars:
                    yield category, star[0]

    def steps(self):
        return [MRStep(mapper=self.mapper_userid, reducer=self.reducer_1)]

if __name__ == '__main__':
    UsersCount.run()

print("--- %s seconds ---" % (time.time() - start_time))