import nanoutils as nano
from interface_client import InterfaceClient
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
from tornado import gen
import logging
import socket
import requests
import json
import time
import datetime
import argparse
import random
import rethinkdb
import hashlib
import uuid
from enum import Enum

# from tornado.concurrent import Future, chain_future

TIMEOUT_ON_DEMAND = 6.0

parser = argparse.ArgumentParser()
parser.add_argument("--rai_node_uri", help='rai_nodes uri, usually 127.0.0.1', default='127.0.0.1')
parser.add_argument("--rai_node_port", help='rai_node port, usually 7076', default='7076')
parser.add_argument("--internal_port", help='internal port which nginx proxys', default='5000')
parser.add_argument("--interface", help='send data to interface (configured in interface.cfg', action='store_true')
parser.add_argument("-v", "--verbose", help='more prints to help debugging', action='store_true')

args = parser.parse_args()

rai_node_address = 'http://{uri}:{port}'.format(uri=args.rai_node_uri, port=args.rai_node_port)

wss_demand = []
wss_precache = []
wss_work = []
wss_timeout = []
hash_to_precache = []
work_tracker = {}
precache_work_tracker = {}
old_hashes = []
blacklist = {}

worker_counter = 0

rethinkdb.set_loop_type("tornado")
connection = rethinkdb.connect("localhost", 28015)

timezone = rethinkdb.make_timezone('+00:00')

def print_time_debug(message):
    if args.verbose:
        print("(DEBUG)", end=' ')
        print_time(message)

def print_time(message):
    print(time.strftime("%d/%m/%Y %H:%M:%S") + " " + str(message))

def print_lists(work=False, demand=False, precache=False, timeout=False):
    if not (work or demand or precache):
        return
    s = "State of lists:"
    if work:
        s += "\n\t\t\twss_work: {}".format(wss_work)
    if demand:
        s += "\n\t\t\twss_demand: {}".format(wss_demand)
    if precache:
        s += "\n\t\t\twss_precache: {}".format(wss_precache)
    if timeout:
        s += "\n\t\t\twss_timeout: {}".format(wss_timeout)
    print_time(s)

def remove_from_timeout(client):
    print_lists(timeout=True)
    print_time("Removing client {} IP from timeout".format(client))
    if client.ip in wss_timeout:
        wss_timeout.remove(client.ip)

def get_all_clients():
    clients = set(wss_demand + wss_precache)
    for client in clients:
        if not client.ws_connection.stream.socket:
            print_time("Removing client, socket is not active: {}".format(client))
            client.close()
            client.remove_from_lists()
            clients.remove(client)
            del client
    return clients

def build_blacklist(filepath='blacklist.txt'):
    try:
        with open(filepath, 'r') as f:
            lines = f.readlines()
    except:
        print('Blacklist file does not exist: {}'.format(filepath))
        return {}

    blacklist = { line.split('\n')[0] for line in lines }
    print('Built blacklist:')
    { print('\t{}'.format(acc)) for acc in blacklist }

    return blacklist

# TODO we would like to not have to use this at all, once all services provide the account
def get_account_from_hash(hash_hex):
    """Returns tuple (account,is_open_block)"""

    get_account = '{ "action" : "block_account", "hash" : "%s"}' % hash_hex
    print_time_debug("action block_account request:\n{}".format(get_account))
    r = requests.post(rai_node_address, data=get_account)
    print_time_debug("action block_account response:\n{}".format(r.text))

    resulting_data = r.json()
    if 'account' in resulting_data:
        account = resulting_data['account'].replace('nano_','xrb_')
        return (account, False)
    elif 'error' in resulting_data:
        if resulting_data['error'] == 'Block not found':
            # This maybe a public key for an Open Block, convert to xrb address
            print_time("Open Block")
            account = nano.hex_to_account(hash_hex)
            print_time("The account is {}".format(account))
            return (account, True)
        else:  # other errors, for instance invalid hash
            return ('Error', None)
    else:
        return ('Error', None)

class WorkState(Enum):
    needs = "0"

class Work(tornado.web.RequestHandler):
    def data_received(self, chunk):
        pass

    @gen.coroutine
    def ws_demand(self, message):
        print_time("Sending via WS Demand")
        # randomise our wss list then cycle through until we manage to send a message
        random.shuffle(wss_demand)
        print_lists(work=1, demand=1, timeout=1)

        # First pass: prioritize on_demand clients only
        for ws in wss_demand:
            if not ws.ws_connection.stream.socket:
                print_time("Web socket does not exist anymore!!!")
                ws.close()
                ws.remove_from_lists()
                del ws
            else:
                if ws not in wss_work and ws.ip not in wss_timeout and ws not in wss_precache:
                    ws.write_message(message)
                    wss_work.append(ws)
                    return ws

        # Second pass: go for on_demand and both
        for ws in wss_demand:
            if ws not in wss_work and ws.ip not in wss_timeout:
                ws.write_message(message)
                wss_work.append(ws)
                return ws

        # no clients
        return None

    @gen.coroutine
    def get_work_via_ws(self, hash_hex, account):
        conn = yield connection

        # Get appropriate threshold value
        # TODO after prioritization PoW is implemented, calculate here an appropriate multiplier
        multiplier = 1.0
        threshold = nano.from_multiplier(nano.NANO_DIFFICULTY, multiplier)
        threshold_str = nano.threshold_to_str(threshold)

        # Try up to 2 times, sending work to another client if the first fails
        error = None
        max_tries = 2
        try_count = 0
        while try_count < max_tries:
            try_count += 1

            # Send request to websocket clients to process
            send_result = yield self.ws_demand('{"hash" : "%s", "type" : "urgent", "threshold" : "%s"}' % (hash_hex,threshold_str))

            # If no clients, mark this account in DB
            if not send_result:
                print_time("No clients to to work for account %s" % account)
                if wss_timeout and try_count != max_tries:
                    print_lists(timeout=1)
                    removed = wss_timeout.pop(0)
                    print_time("Trying again after removing the oldest IP in wss_timeout: {}".format(removed))
                    continue
                else:
                    error = 'no_clients'
                    break

            # Place this hash in the work tracker so it can be updated in the client handler
            client_id = None
            work_tracker[hash_hex] = -1
            t_start = time.time()
            print_time("Waiting for work...")
            while time.time() - t_start < TIMEOUT_ON_DEMAND:
                try:
                    if work_tracker.get(hash_hex) != -1:
                        work_output, client_id = work_tracker.pop(hash_hex)
                        print_time_debug("Hash handled in {} seconds: {}".format(time.time()-t_start, hash_hex))
                        raise gen.Return((work_output, client_id, multiplier))
                except KeyError:
                    print_time_debug("Hash was removed from work_tracker due to an error: {}".format(hash_hex))
                    error = 'failed_clients'
                    break
                else:
                    yield gen.sleep(0.01)

            # Took too long, add to timeout list
            client_to_timeout = send_result
            print_time("Placing {} in timeout for 2 hours".format(client_to_timeout))
            wss_timeout.append(client_to_timeout.ip)
            tornado.ioloop.IOLoop.current().add_timeout(time.time() + 120*60, lambda: remove_from_timeout(client_to_timeout))

        if error:
            if error == 'no_clients':
                raise gen.Return(('no clients', None, None))

            elif error == 'failed_clients':
                raise gen.Return(('error', None, None))

        else:
            # No specific error by client handler, simply a timeout reached
            raise gen.Return(('timeout', None, None))

    @gen.coroutine
    def post(self):
        receive_time = datetime.datetime.now(timezone)
        post_data = json.loads(self.request.body.decode('utf-8'))
        if 'hash' in post_data:
            hash_hex = post_data['hash'].upper()
        elif 'accounts' not in post_data:
            return_json = '{"status" : "no hash"}'
            print_time(return_json)
            self.write(return_json)
            return

        conn = yield connection

        # 1 Check key is valid
        if 'key' in post_data:
            print_time("found API key")
            key = post_data['key']
            key_hashed = hashlib.sha512(key.encode('utf-8')).hexdigest()
            service_data = yield rethinkdb.db("pow").table("api_keys").filter({"api_key": key_hashed}).nth(0).default(False).run(conn)
            if not service_data:
                print_time("incorrect API key")
                return_json = '{"status" : "incorrect key"}'
                print_time(return_json)
                self.write(return_json)
                return
            else:
                print_time("Correct API key from %s - continue" % service_data['username'])
        else:
            print_time("no API key")
            return_json = '{"status" : "no key"}'
            print_time(return_json)
            self.write(return_json)
            return

        # Are they sending a precache list?
        if 'accounts' in post_data:
            accounts_precache = set(post_data['accounts'])
            print_time("Request to precache for {} accounts".format(len(accounts_precache)))

            # Take out those that are not in DB
            accounts_in_db = set()
            accounts_expr = rethinkdb.expr(accounts_precache)
            accounts_data = yield rethinkdb.db("pow").table("hashes").filter(lambda doc: accounts_expr.contains(doc['account'])).run(conn)
            while (yield accounts_data.fetch_next()):
                item = yield accounts_data.next()
                accounts_in_db.add(item["account"])

            not_in_db = accounts_precache.difference(accounts_in_db)
            print_time("{} accounts already in DB, adding the other {}".format(len(accounts_in_db), len(not_in_db)))

            # Get the public keys
            bad_accounts = set()
            public_keys = dict()
            for account in not_in_db:
                get_key = '{ "action" : "account_key", "account" : "%s"}' % account
                r = requests.post(rai_node_address, data=get_key)
                r = r.json()
                if 'error' in r:
                    # invalid account
                    bad_accounts.add(account)
                else:
                    public_keys[account] = r['key']

            # Filter out the bad accounts
            filtered_accounts = not_in_db.difference(bad_accounts)

            if bad_accounts:
                print_time("{} accounts given to precache are not valid:\n{}".format(len(bad_accounts), bad_accounts))
                print_time("{} were valid".format(len(filtered_accounts)))

            # Add to DB
            results = yield rethinkdb.db("pow").table("hashes").insert(
                [{"work": WorkState.needs.value, "hash": public_keys[account], "account": account, "unopened_account": True} for account in filtered_accounts]
            ).run(conn)

            # Send for precaching
            hash_to_precache.extend(public_keys.values())

            # Send feedback to service
            return_json = json.dumps({"added": list(filtered_accounts), "already_tracked": list(accounts_in_db), "bad_accounts": list(bad_accounts)})
            print_time_debug(return_json)
            self.write(return_json)
            return

        # 2 Do they provide the account besides the hash?
        account = post_data.get('account') or None
        if account:
            print_time('Account provided: {}'.format(account))
            account = account.replace('nano_','xrb_')
            if account.find('xrb_') == -1:
                print_time_debug('NOTIFY SERVICE! Invalid account provided {}'.format(account))
                account = None
        else:
            print_time_debug('NOTIFY SERVICE! Ask to provide accounts')

            # Get account to setup DB entries and check if invalid hash
            account, is_open_block = get_account_from_hash(hash_hex)
            if account == 'Error':
                return_json = '{"status" : "bad hash"}'
                print_time(return_json)
                self.write(return_json)
                return

        # Update entry
        new_count = int(service_data['count']) + 1
        yield rethinkdb.db("pow").table("api_keys").filter(rethinkdb.row['api_key'] == key_hashed).update(
            {"count": new_count, "provides_accounts": post_data.get('account')!=None}).run(conn)

        # 3 Do we have hash in db? If not (or work is not provided) handle on demand
        hash_data = yield rethinkdb.db("pow").table("hashes").filter({"account": account}).nth(0).default(False).run(conn)
        if hash_data and hash_data['hash'] == hash_hex:
            print_time('Found cached work value %s' % hash_data['work'])
            work_output = hash_data['work']
            new_entry = False
            if work_output == WorkState.needs.value:
                print_time("Empty work, get new on demand")
                work_output, client_id, multiplier = yield self.get_work_via_ws(hash_hex, account)
                work_type = 'O'
            else:
                client_id = hash_data.get('last_worker') or None
                if 'threshold' in hash_data:
                    multiplier = nano.to_multiplier(nano.NANO_DIFFICULTY, nano.threshold_from_str(hash_data['threshold']))
                else:
                    multiplier = 1.0
                work_type = 'P'
        elif not hash_data:
            print_time('Account not in DB, creating entry and getting on demand...')
            new_entry = True
            work_output, client_id, multiplier = yield self.get_work_via_ws(hash_hex, account)
            work_type = 'O'
        else: # hash_data but hash is old
            print_time('Old hash, getting on demand...')
            new_entry = False
            work_output, client_id, multiplier = yield self.get_work_via_ws(hash_hex, account)
            work_type = 'O'

        complete_time = datetime.datetime.now(timezone)

        # 4 Return work
        if work_output in ['timeout', 'no clients', 'error']:
            return_json = '{"status" : "%s"}' % work_output
            work_ok = False
        else:
            return_json = '{"work" : "%s"}' % work_output
            work_ok = True

        print_time(return_json)
        self.write(return_json)

        # DB update
        if work_ok:
            threshold = nano.from_multiplier(nano.NANO_DIFFICULTY, multiplier)
            threshold_str = nano.threshold_to_str(threshold)

            if new_entry:
                yield rethinkdb.db("pow").table("hashes").insert(
                    {"account": account, "last_worker": client_id, "hash": hash_hex, "work": work_output, "threshold": threshold_str, "unopened_account": False}).run(conn)
            else:
                yield rethinkdb.db("pow").table("hashes").filter({"account": account}).update(
                    {"hash": hash_hex, "last_worker": client_id, "work": work_output, "threshold": threshold_str, "unopened_account": False}).run(conn)
        else:
            if new_entry:
                yield rethinkdb.db("pow").table("hashes").insert(
                    {"account": account, "hash": hash_hex, "work": WorkState.needs.value, "unopened_account": False}).run(conn)
            else:
                yield rethinkdb.db("pow").table("hashes").filter({"account": account}).update(
                    {"hash": hash_hex, "work": WorkState.needs.value, "unopened_account": False}).run(conn)

        # Interface update
        if work_ok and interface:

            # Random request id
            request_id = str(uuid.UUID(int=random.SystemRandom().getrandbits(128)))

            # Send to interface
            service_id = service_data['id']
            interface.pow_update(request_id, service_id, client_id, work_type, multiplier, receive_time, complete_time, new_entry)

class WSHandler(tornado.websocket.WebSocketHandler):

    worker_counter = 0

    def __init__(self, *args, **kwargs):
        WSHandler.worker_counter += 1
        self.id = WSHandler.worker_counter
        self.type = ''
        self.address = ''
        self.ip = ''
        super().__init__(*args, **kwargs)

    def __repr__(self):
        return '{id} ({ip}, {type}, {state})'.format(id=self.id or 'unknown id', ip=self.ip or 'unknown ip', type=self.type or 'unknown type',
                                       state='timeout' if self.ip in wss_timeout else 'busy' if self in wss_work else 'free')

    @staticmethod
    def validate_work(hash_hex, work, threshold):
        get_validation = '{ "action" : "work_validate", "hash" : "%s", "work": "%s", "threshold": "%s" }' % (hash_hex, work, threshold)
        r = requests.post(rai_node_address, data=get_validation)
        resulting_validation = r.json()
        if 'error' in resulting_validation:
            print_time_debug(get_validation)
            raise gen.Return('Error in validation: {}'.format(resulting_validation))
        return int(resulting_validation['valid'])

    def open(self):
        print_time('New worker connected - {}'.format(self.id))
        self.ip = self.request.remote_ip
        print_time('IP: {}'.format(self.ip))

    @gen.coroutine
    def on_message(self, message):
        print_time('Message from worker {}: {}'.format(self.id, message))
        try:
            ws_data = json.loads(message)
            if 'address' not in ws_data:
                raise Exception('Incorrect data from client: {}'.format(ws_data))

            self.address = ws_data['address']

            if 'work_type' in ws_data:
                # Setup message handling

                # remove from any lists
                self.remove_from_lists()

                # restrict clients per IP
                connected_clients = get_all_clients()
                same_ip = list(filter(lambda c: c.ip == self.ip, connected_clients))
                if len(same_ip) >= 2:  # this client is not yet in the lists
                    print_time("Client attempted to connect more than 2 clients: IP: {} , already in list: {} , new client: {}".format(self.ip, same_ip, self))
                    self.write_message('{"status": "error", "description": "Maximum of 2 clients allowed"}')
                    self.close()
                    return

                # handle setup message for work type
                if ws_data['address'] in blacklist:
                    print_time("Blacklisted: {}".format(ws_data['address']))
                else:
                    work_type = ws_data['work_type']
                    print_time("Found work_type -> {}".format(work_type))
                    try:
                        self.update_work_type(work_type)
                        self.write_message('{"status": "success"}')
                    except Exception as e:
                        print_time(e)
                        self.write_message('{"status": "error", "description": "%s"}' % e)
            else:
                if 'hash' not in ws_data or 'work' not in ws_data:
                    raise Exception('Incorrect data from client: {}'.format(ws_data))

                if self not in wss_precache and self not in wss_demand:
                    self.write_message('{"status": "error", "description": "Must setup first"}')
                    return

                # handle work message
                hash_hex = ws_data['hash'].upper()
                work = ws_data['work']
                payout_account = ws_data['address'].lower()

                if work == 'error':
                    raise Exception("'Something wrong with the client, work returned as error'")

                if hash_hex in work_tracker:
                    this_work_type = 'urgent'
                elif hash_hex in precache_work_tracker:
                    this_work_type = 'precache'
                else:
                    print_time("Work was given for hash {} but this hash was not in any of the work trackers".format(hash_hex))
                    if hash_hex in old_hashes:
                        conn = yield connection
                        print_time("Hash was an old hash given to this client, we should still increase client count but not update the account entry")
                        clients_data = yield rethinkdb.db("pow").table("clients").filter(
                            rethinkdb.row['account'] == payout_account).nth(0).default(False).run(conn)
                        if clients_data:
                            client = clients_data
                            count = int(client['count'])
                            yield rethinkdb.db("pow").table("clients").filter(rethinkdb.row['account'] == payout_account).update(
                                {"count": count + 1}).run(conn)
                    else:
                        print_time("Hash not in old_hashes, so could be a client trying to do fake work. Rejected and return here")
                        return

                # check the threshold at which this was computed
                # defaults to NANO_DIFFICULTY if not found (e.g. early entries in DB)
                conn = yield connection
                data = yield rethinkdb.db("pow").table("hashes").filter(rethinkdb.row['hash'] == hash_hex).nth(0).default(False).run(conn)
                if data:
                    threshold_str = data.get("threshold") or nano.threshold_to_str(nano.NANO_DIFFICULTY)
                else:
                    threshold_str = nano.threshold_to_str(nano.NANO_DIFFICULTY)

                # validate the work from client
                valid = self.validate_work(hash_hex, work, threshold_str)
                if valid:
                    if hash_hex in hash_to_precache:
                        hash_to_precache.remove(hash_hex)

                    if this_work_type == 'urgent':
                        # Update the work tracker so that the service wait loop knows it is done
                        work_tracker[hash_hex] = (work, payout_account)
                    else:
                        if hash_hex in precache_work_tracker:
                            precache_work_tracker.pop(hash_hex)

                        # Update DB here directly
                        yield rethinkdb.db("pow").table("hashes").filter({"hash": hash_hex}).update(
                            {"work": work, "threshold": threshold_str, "last_worker": payout_account, "unopened_account": False}).run(conn)

                    # Add work record to client database to allow payouts
                    clients_data = yield rethinkdb.db("pow").table("clients").filter(
                        rethinkdb.row['account'] == payout_account).nth(0).default(False).run(conn)
                    if clients_data:
                        client = clients_data
                        count = int(client['count'])
                        count_type = int(client['{}_count'.format(this_work_type)])
                        yield rethinkdb.db("pow").table("clients").filter(
                            rethinkdb.row['account'] == payout_account).update(
                                {"count": count + 1, "{}_count".format(this_work_type): count_type + 1, "time": time.time()}).run(conn)
                    else:
                        other_work_type = 'urgent' if this_work_type == 'precache' else 'precache'
                        yield rethinkdb.db("pow").table("clients").insert(
                               {"account": payout_account, "count": 1, "{}_count".format(this_work_type): 1,
                                       "{}_count".format(other_work_type): 0, "time": time.time()}).run(conn)
                    # Remove from work list
                    if self in wss_work:
                        print_time("Removing {} from wss_work".format(self))
                        wss_work.remove(self)

                else:
                    raise Exception("Failed to validate work - {} for worker {}".format(valid, self))

        except Exception as e:
            print_time("Error {}".format(e))
            if self in wss_work:
                print_time("Removing {} from wss_work after exception".format(self))
                wss_work.remove(self)

            try:
                # Remove from work tracker, the service wait loop can decide what to do (for instance give work to someone else)
                if hash_hex in work_tracker:
                    work_tracker.pop(hash_hex)

                if hash_hex in precache_work_tracker:
                    precache_work_tracker.pop(hash_hex)
                    if hash_hex not in hash_to_precache:
                        hash_to_precache.append(hash_hex)
            except:
                pass

    def update_work_type(self, work_type):

        if work_type == 'any':
            # Add to both demand and precache
            wss_demand.append(self)
            wss_precache.append(self)
            self.type = 'B'
        elif work_type == 'precache_only':
            # Add to precache
            wss_precache.append(self)
            self.type = 'P'
        elif work_type == 'urgent_only':
            # Add to demand
            wss_demand.append(self)
            self.type = 'O'
        else:
            raise Exception('Invalid work type {}'.format(work_type))

    def on_close(self):
        print_time('Worker disconnected - {}'.format(self.id))
        self.remove_from_lists()

    def remove_from_lists(self):
        for l in [wss_work, wss_demand, wss_precache]:
            if self in l:
                l.remove(self)

    def check_origin(self, origin):
        return True

application = tornado.web.Application([
    (r'/group/', WSHandler),
    (r"/work", Work),
])

@gen.coroutine
def push_precache():
    hash_count = 0
    work_count = 0

    # Get appropriate threshold value
    # TODO what is a good threshold value for precaching?
    multiplier = 1.0
    threshold = nano.from_multiplier(nano.NANO_DIFFICULTY, multiplier)
    threshold_str = nano.threshold_to_str(threshold)

    if hash_to_precache:
        print_time("Got precache work to push")
        random.shuffle(wss_precache)
        print_lists(work=1, precache=1)
        # if all precachers are doing work... no free workers
        if set(wss_precache).issubset(set(wss_work)):
            print_time("No clients available to push precache")
            return

    for hash_hex in hash_to_precache:
        hash_count = hash_count + 1
        hash_handled = False
        for work_clients in wss_precache:  # type: WSHandler
            print_time("Sending via WS Precache")
            print_lists(work=1, precache=1)
            try:
                if not work_clients.ws_connection.stream.socket:
                    print_time("Web socket does not exist anymore!!!")
                    work_clients.close()
                    work_clients.remove_from_lists()
                    del work_clients
                else:
                    if work_clients not in wss_work:
                        work_count = work_count + 1
                        message = '{"hash" : "%s", "type" : "precache", "threshold" : "%s"}' % (hash_hex, threshold_str)
                        work_clients.write_message(message)
                        wss_work.append(work_clients)
                        hash_to_precache.remove(hash_hex)
                        precache_work_tracker[hash_hex] = time.time()
                        hash_handled = True
                        break

            except Exception as e:
                print_time('Error when sending via WS precache: {}'.format(e))
                if work_clients in wss_precache:
                    wss_precache.remove(work_clients)
                    print_time('Client {} removed from precache work'.format(work_clients.id))
                if work_clients in wss_work:
                    wss_work.remove(work_clients)

        if hash_handled:
            print_time("Hash was given to a precache client")
        else:
            print_time("Precache not handled - no free workers?")
            break

@gen.coroutine
def precache_update():
    t_start = time.time()
    count_updates = 0
    up_to_date = 0
    too_old = 0
    not_up_to_date = 0
    conn = yield connection
    #   print_time("precache_update")
#    precache_data = yield rethinkdb.db("pow").table("hashes")['account'].distinct().run(conn)
    precache_data = yield rethinkdb.db("pow").table("hashes").run(conn)
    if not precache_data:
        print_time("Failed to retrieve data from DB in precache_update")
        return

    count_unique = 0
    count_all = 0
    account_to_hash = dict()
    while (yield precache_data.fetch_next()):
        item = yield precache_data.next()
        account = item['account']
        if account not in account_to_hash:
            account_to_hash[account] = (item['hash'], item['work'])
            count_unique += 1
        count_all += 1

    accounts = list(account_to_hash.keys())
    print('Total {}, unique {}, duplicate: {}'.format(count_all, count_unique, count_all-count_unique))

    '''
    Get frontiers for all accounts in a single RPC call
    - if any account is not correct (wrong checksum), this will return an error - so it should be absolutely avoided
    - if an account is valid but no frontier is found by the node, it is likely an open block
    - since it could be simply that the "open account" block was not processed by the service, we will not be deleting open blocks here anymore
    '''
    # TODO if needed, then do a cleanup of wrong blocks every now and then, e.g. every 24h
    for i in range(0, len(accounts), 100):
        get_frontiers = json.dumps({"action": "accounts_frontiers", "accounts": accounts[i:i+99]})

        r = requests.post(rai_node_address, data=get_frontiers)
        frontiers = r.json()
        if 'frontiers' not in frontiers:
            print_time("Error getting account frontiers in precache_update: {}".format(frontiers))
            return

        frontiers = frontiers['frontiers']
        if type(frontiers) == str:    continue

        # this code would get the open block mistakes
        # open_block_mistakes = [a for a in accounts if a not in frontiers]

        for account, frontier in frontiers.items():
            count_updates += 1

            old_hash, work = account_to_hash[account]
            if old_hash == frontier:
                up_to_date += 1
                if old_hash in precache_work_tracker:
                    time_sent = precache_work_tracker[old_hash]
                    if time.time() - time_sent > 1000.0:
                        # probably client disconnected while doing precache work, we need to queue it again
                        too_old += 1
                        precache_work_tracker.pop(old_hash)
                        hash_to_precache.append(old_hash)
                else:
                    if work == WorkState.needs.value:
                        # this was precache work left undone since the last server restart
                        if old_hash not in hash_to_precache:
                            hash_to_precache.append(old_hash)

            else:
                not_up_to_date += 1
                if frontier not in hash_to_precache:
                    hash_to_precache.append(frontier)
                    if old_hash in precache_work_tracker:
                        # even if returned, the hash will not be up-to-date, so better remove from tracking and reject it later
                        precache_work_tracker.pop(old_hash)

                        # add to a list of old hashes. since we asked the clients, we should update their count later
                        old_hashes.append(old_hash)

                    yield rethinkdb.db("pow").table("hashes").filter(rethinkdb.row['account'] == account).update(
                        {"work": WorkState.needs.value, "hash": frontier, "unopened_account": False}).run(conn)

    print_time("Count: {:d}, Up to date: {:d}, Too old: {:d}, Not up to date: {:d}".format(
                count_updates, up_to_date, too_old, not_up_to_date))

    print_time_debug("precache_update took: {:.4f} seconds".format(time.time()-t_start))

@gen.coroutine
def update_interface_clients():
    conn = yield connection
    connected_clients = get_all_clients()
    # seen = set()
    # unique_clients_by_account = [c for c in connected_clients if c.address not in seen and not seen.add(c.address)]
    accounts = [c.address for c in connected_clients]
    accounts_expr = rethinkdb.expr(accounts)
    counts_by_type = dict()

    data = yield rethinkdb.db("pow").table("clients").filter(lambda row: accounts_expr.contains(row["account"])).run(conn)
    while (yield data.fetch_next()):
        client_data = yield data.next()
        counts_by_type[client_data["account"]] = {"precache": client_data["precache_count"], "urgent": client_data["urgent_count"]}

    clients = list(map(lambda c: {
        'client_id': c.address+'_'+c.type,
        'client_address': c.address,
        'client_type': c.type,
        'client_demand_count': counts_by_type[c.address]["urgent"] if c.address in counts_by_type else 0,
        'client_precache_count': counts_by_type[c.address]["precache"] if c.address in counts_by_type else 0
    }, connected_clients))

    interface.clients_update(clients)
    print_time_debug("Updated interface clients")

@gen.coroutine
def update_interface_services():
    conn = yield connection
    registered_services = yield rethinkdb.db("pow").table("api_keys").run(conn)
    if registered_services:
        services = list(map(lambda s: {
            'service_id': s.get('id'),
            'service_name': (s.get('display_name') or s.get('username')) if s.get('public') else None,
            'service_web': (s.get('website') or '') if s.get('public') else ''
        }, registered_services.items))

        interface.services_update(services)
        print_time_debug("Updated interface services")
    else:
        print_time_debug("Could not update interface services")

@gen.coroutine
def initial_db_update():
    conn = yield connection
    unopened_data = yield rethinkdb.db("pow").table("hashes").filter({"unopened_account": True, "work": WorkState.needs.value}).run(conn)
    unopened_accounts = set()
    while (yield unopened_data.fetch_next()):
        item = yield unopened_data.next()
        unopened_accounts.add(item['hash'])
    if unopened_accounts:
        print_time("Adding {} unopened accounts to precache".format(len(unopened_accounts)))
        hash_to_precache.extend(unopened_accounts)
    print_time("Initial DB update done")

if __name__ == "__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(int(args.internal_port))
    myIP = socket.gethostbyname(socket.gethostname())

    # setup interface
    if args.interface:
        interface = InterfaceClient()
        if not interface.read_config('interface.cfg'):
            print_time('INTERFACE DISABLED')
            interface = None
        else:
            print_time('Interface is setup -> {}'.format(interface.server))
    else:
        interface = None

    # initial DB check
    tornado.ioloop.IOLoop.current().run_sync(initial_db_update)

    blacklist = build_blacklist('blacklist.txt')

    print_time('*** Websocket Server Started at %s***' % myIP)
    pc = tornado.ioloop.PeriodicCallback(precache_update, 15000)
    pc.start()
    push = tornado.ioloop.PeriodicCallback(push_precache, 3000)
    push.start()
    if interface:
        # clients update every 10 seconds
        icli = tornado.ioloop.PeriodicCallback(update_interface_clients, 10000)
        icli.start()

        # services update every 60 seconds
        iserv = tornado.ioloop.PeriodicCallback(update_interface_services, 60000)
        iserv.start()

    main_loop = tornado.ioloop.IOLoop.instance()

    try:
        main_loop.start()
    except KeyboardInterrupt:
        pass


import socket
import threading
import os
import logging
import datetime

def get_datetime():
    '''Returns a dictionary containing the date and time

    dt['time'] - contains current time in hh:mm format(24 hrs)
    dt['date'] - contains current date as dd-mm-yyyy format
    '''
    dt = {}

    now = datetime.datetime.now()
    dt['time'] = now.strftime('%H:%M')
    dt['date'] = now.strftime('%d-%m-%Y')

    return dt

def get_cmd(cmd, cmds_list):
    '''Search the command (cmd), eg. !twitter in the commands list (cmds_list)
    and try to import its module

    The return value is the function that represents the command or None if the
    command doesn't exist or it's not defined properly
    '''
    if cmd not in cmds_list:
        return None

    try: # the command's module needs to be imported from 'cmds/'
        mod = 'cmds.' + cmd
        mod = __import__(mod, globals(), locals(), [cmd])
    except ImportError as e: # inexistent module
        logging.error(err.C_INEXISTENT.format(cmd) + str(e))
        return None

    try:
        # the name of the command is translated into a function's name,
        # then returned
        callable_cmd = getattr(mod, cmd)
    except AttributeError as e:
        # function not defined in module
        logging.error(err.C_INVALID.format(cmd) + str(e))
        return None

    return callable_cmd

def run_cmd(sock, executor, to, cmd, arguments):
    '''Create a future object for running a command asynchronously and add a
    callback to send the response of the command back to irc
    '''
    def cb(f):
        try:
            response = f.result()
        except Exception as e: # TODO: raise a specific exception form the cmds
            response = err.C_EXCEPTION.format(cmd.__name__)
            logging.error(e)

        send_response(response, to, sock)

    future = executor.submit(cmd, arguments)
    future.add_done_callback(cb)

send_response_lock = threading.Lock()
def send_response(response, destination, s):
    '''Attempt to send the response to destination through the s socket
    The response can be either a list or a string, if it's a list then it
    means that the module sent a command on its own (eg. PART)

    The destination can be passed using the send_to function

    True is returned upon sending the response, None if the response was empty
    or False if an error occurred while sending the response
    '''
    if response is not None and len(response): # send the response and log it
        if type(response) == type(str()):
            # the module sent just a string so
            # I have to compose the command

            # a multi-line command must be split
            crlf_pos = response[:-2].find('\r\n')
            while -1 != crlf_pos:
                crlf_pos = crlf_pos + 2 # jump over '\r\n'
                response = response[:crlf_pos] + \
                        destination + response[crlf_pos:]

                next_crlf_pos = response[crlf_pos:-2].find('\r\n')
                if -1 != next_crlf_pos:
                    crlf_pos = crlf_pos + next_crlf_pos
                else:
                    crlf_pos = -1

            response = destination + response
        else: # the module sent a command like WHOIS or KICK
            response = ' '.join(response)

        # append CRLF if not already appended
        if '\r\n' != response[-2:]:
            response = response + '\r\n'

        try:
            with send_response_lock:
                s.send(response)
        except IOError as e:
            logging.error('Unexpected error while sending the response: {0}\n'
                .format(e))

            return False

        logging.debug(response)

        return True

    return None


def partition(arr,p,r):
    x=arr[r]
    i=p-1

    for j in range(p,r):
        if arr[j]>x:
            i+=1
            arr[i],arr[j]=arr[j],arr[i]
    arr[i+1],arr[r]=arr[r],arr[i+1]
    return i+1
def Quicksort(arr,p,r):
    if p<r:
        q=partition(arr,p,r)
        Quicksort(arr,p,q-1)
        Quicksort(arr,q+1,r)

def findlargestpalindrome(s):
    l=list(s)

    dic={}

    for x in l:
        if x in dic:
            dic[x]+=1
        else:
            dic[x]=1

    arr=[]
    for x in dic:
        arr.append(int(x))

    odd=0

    for x in dic:
        if dic[x]%2==1:
            odd+=1
    if odd>1:
        print('not possible')
        return

    Quicksort(arr,0,len(arr)-1)

    number=[]

    for i in range(len(arr)):
        number.append(str(arr[i]))

    for j in range(len(arr)-1,-1,-1):
        number.append(str(arr[j]))

    print(''.join(number))

s = "313551"
findlargestpalindrome(s)

s = "313351"
findlargestpalindrome(s)


menu = 1
lista = []
while menu != 5:
    print("1.- AGREGAR NÚMERO\n2.- FILTRAR NÚMEROS PARES\n3.- FILTRAR NÚMEROS IMPARES\n4.- BORRAR LISTA ACTUAL\n5.- SALIR DEL PROGRAMA")
    menu = input("Ingrese su opción: ")
    while type(menu) == str:
        try:
            menu = int(menu)
            if menu>5 or menu<1:
                menu = input("Opción ingresada no es válida. Ingrese nuevamente su opción: ")
        except ValueError:
            menu = input("Opción ingresada no es válida. Ingrese nuevamente su opción: ")
    if menu == 1:
        num = input("Ingrese el número que desea agregar a la lista: ")
        while type(num) == str:
            try:
                num = int(num)
            except ValueError:
                num = input("Valor ingresado no es válida. Ingrese nuevamente su número: ")
        lista.append(num)
    #PARES
    elif menu == 2:
        pares = []
        for n in lista:
            if (n%2) == 0:
                pares.append(n)
        if len(pares) != 0:
            pares.sort()
            print(f"Los pares ingresados son {pares}.")
        else:
            print("No existen números pares en su lista.")
    #IMPARES
    elif menu == 3:
        impares = []
        for n in lista:
            if (n%2) != 0:
                impares.append(n)
        if len(impares) != 0:
            impares.sort()
            print(f"Los impares ingresados son {impares}.")
        else:
            print("No existen números impares en su lista.")
    elif menu == 4:
        lista = []
    else:
        print("Usted ha salido del programa. ¡Gracias por haberlo usado!")

Experiencia 3/Otros ejercicios/Filtrar pares e impares.py


# -*- coding: utf-8 -*-
"""
Created on Tue Jan 05 11:53:50 2018
Useful fonctions and classes to connect to instruments in general using GPIB,
COM port or Prologix controller

"""
import logging

import glob
import sys

try:
    import serial

    serial_available = True
except ImportError:
    serial_available = False
    print("pyserial package not installed, run 'pip install pyserial'")

try:
    import visa

    visa_available = True
except ImportError:
    visa_available = False
    print("pyvisa package not installed, run 'pip install pyvisa'")

INTF_VISA = "pyvisa"
INTF_PROLOGIX = "prologix"
INTF_GPIB = INTF_PROLOGIX
INTF_SERIAL = "serial"

INTF_NONE = "None"

PROLOGIX_COM_PORT = "COM3"

def list_gpib_ports():
    """ use pyvisa to list the GPIB ports """

    rm = visa.ResourceManager()
    available_ports = rm.list_resources()
    temp_ports = []
    for port in available_ports:
        if "GPIB" in port:
            temp_ports.append(str(port))
    available_ports = temp_ports

    return available_ports

def list_serial_ports(max_port_num=20):
    """ Lists serial port names from COM1 to COM20 in windows platform and
    lists all serial ports on linux platform

        :raises EnvironmentError:
            On unsupported or unknown platforms
        :returns:
            A list of the serial ports available on the system
    """
    if sys.platform.startswith("win"):
        ports = ["COM%s" % (i + 1) for i in range(max_port_num)]
    elif sys.platform.startswith("linux") or sys.platform.startswith("cygwin"):
        # this excludes your current terminal "/dev/tty"
        ports = glob.glob("/dev/tty[A-Za-z]*")
    elif sys.platform.startswith("darwin"):
        ports = glob.glob("/dev/tty.*")
    else:
        raise EnvironmentError("Unsupported platform")

    result = []
    for port in ports:
        try:
            s = serial.Serial(port, 9600, timeout=0.1)
            result.append(str(port))
            s.close()
        except (OSError):
            pass
    return result

def refresh_device_port_list(debug=False):
    """ Load VISA resource list for use in combo boxes """

    if debug:

        return ["GPIB0::%i" % i for i in range(30)]

    else:

        return list_gpib_ports() + list_serial_ports()

def find_prologix_ports():
    """
        go through the serial ports and wee which one returns the prologix
        version command
    """
    serial_ports = list_serial_ports()
    result = []
    for port in serial_ports:
        try:
            s = serial.Serial(port, 9600, timeout=0.2)
            s.write("++mode 1\n++auto 0\n++ver\n".encode())
            answer = s.readline()
            prologix_controller = "Prologix GPIB-USB Controller"
            prologix_controller = prologix_controller.encode()
            if prologix_controller in answer:
                result.append(port)
        except (OSError, serial.SerialException):
            pass

    return result

def test_prologix_controller_creation_with_com(com_port=None):
    if com_port is None:
        com_port = "COM3"

    pc = PrologixController(com_port)
    print(pc)
    print(pc.get_open_gpib_ports())

def test_prologix_controller_creation_with_wrong_com():
    pc = PrologixController("COM1")
    print(pc)
    print(pc.get_open_gpib_ports())

def test_prologix_controller_creation_with_no_arg_conflict():
    pc = PrologixController()
    print(pc)
    pc2 = PrologixController()
    print(pc2)

class PrologixController(object):
    connection = None

    def __init__(
        self, com_port=None, mock=False, auto=1, baud_rate=9600, timeout=5, **kwargs
    ):

        self.mock = mock

        if not self.mock:
            if com_port is None:
                # the user didn't provide a COM port, so we look for one
                com_port = find_prologix_ports()

                if com_port != []:
                    if len(com_port) > 1:
                        logging.warning(
                            "There is more than one Prologix \
                         controller, we are connecting to %s"
                            % (com_port[0])
                        )

                    com_port = com_port[0]
                    print("... found a Prologix controller on the port '%s'" % com_port)

                    self.connection = serial.Serial(
                        com_port,
                        baud_rate,
                        xonxoff=True,
                        stopbits=serial.STOPBITS_TWO,
                        timeout=timeout,
                    )
                else:
                    self.connection = None
                    print("There is no Prologix controller to connect to")
            else:

                try:
                    self.connection = serial.Serial(
                        com_port,
                        baud_rate,
                        xonxoff=True,
                        stopbits=serial.STOPBITS_TWO,
                        timeout=timeout,
                    )
                except serial.serialutil.SerialException:
                    self.connection = None
                    print("The port %s is not attributed to any device" % com_port)

            if self.connection is not None:
                # set the connector in controller mode and let the user
                self.write("++mode 1")
                # auto == 1 : ask for read without sending another command.
                # auto == 0 : simply send the command.
                self.auto = auto
                self.write("++auto %i" % self.auto)

                # check the version
                self.write("++ver")
                # important not to use the self.readline() method
                # at this stage if auto == 0, otherwise the connector is going
                # to prompt the instrument for a reading an generate an error
                version_number = (self.connection.readline()).decode()

                if "Prologix GPIB-USB Controller" not in version_number:
                    self.connection = None
                    print(
                        "The port %s isn't related to a Prologix controller "
                        "(try to plug and unplug the cable if it is there "
                        "nevertheless)" % (com_port)
                    )
                    print(version_number)

                print(
                    "%s is connected on the port '%s'" % (version_number[:-2], com_port)
                )
            else:
                print("The connection to the Prologix connector failed")

    def __str__(self):
        if self.connection is not None:
            self.write("++ver")
            return (self.connection.readline()).decode()
        else:
            return ""

    def controller_id(self):
        return self.__str__()

    def write(self, cmd):
        """use serial.write"""
        # add a new line if the command didn't have one already
        if not cmd.endswith("\n"):
            cmd += "\n"
        if self.connection is not None:
            #  print("Prologix in : ", cmd)
            self.connection.write(cmd.encode())

    def read(self, num_bit):
        """use serial.read"""
        if self.connection is not None:
            if not self.auto:
                self.write("++read eoi")
            answer = self.connection.read(num_bit)
            # print("Prologix out (read) : ", answer)
            return (answer).decode()
        else:
            return ""

    def readline(self):
        """use serial.readline"""
        if self.connection is not None:
            if not self.auto:
                self.write("++read eoi")
            answer = self.connection.readline()
            # print("Prologix out (readline): ", answer)
            return answer.decode()
        else:
            return ""

    def timeout(self, new_timeout=None):
        """
        query the timeout setting of the serial port if no argument provided
        change the
        """
        if self.connection is not None:
            if new_timeout is None:
                return self.connection.timeout
            else:
                old_timeout = self.connection.timeout
                self.connection.timeout = new_timeout
                return old_timeout

    def get_open_gpib_ports(self, num_ports=30):
        """Finds out which GPIB ports are available for prologix controller"""
        open_ports = []

        if not self.mock:

            # sets the timeout to quite fast
            old_timeout = self.timeout(0.1)
            # iterate the ports number
            for i in range(num_ports + 1):

                # change the GPIB address on the prologix controller
                # prove if an instrument is connected to the port
                self.write("++addr %i\n*IDN?\n" % i)

                # probe the answer
                s = self.readline()

                # if it is longer than zero it is an instrument
                # we store the GPIB address
                if len(s) > 0:
                    open_ports.append("GPIB0::%s" % (i))

            # resets the timeout to its original value
            self.timeout(old_timeout)
        #        print "Time out is", self.timeout()

        return open_ports


# Chapter04_03
# 파이썬 반복문
# While 실습

# while <expr>:
#   <statement(s)>

# 예제 1

n = 5
while n > 0:
    print(n)
    n = n - 1                       # 5 4 3 2 1

# 예제2
a = ['foo', 'bar', 'baz']

while a:
    print(a.pop())                  # baz bar foo

# if 중첩
# 예제3
# break , continue

n = 5
while n > 0:
    n -= 1
    if n == 2:
        break
    print(n)
print('Loop Ended.')                # 4 3 Loop Ended.
print()

# 예제4
m = 5
while m > 0:
    m -= 1
    if m == 2:
        continue
    print(m)
print('Loop Ended.')                # 4 3 1 0 Loop Ended.

# 예제5
i = 1

while i <= 10:
    print('i :',i)                  # 1 2 3 4 5 6
    if i == 6:
        break
    i += 1

# While - else 구문

# 예제6
n = 10
while n > 0:
    n -= 1
    print(n)                        # 9 8 7 6 5
    if n == 5:
        break
else:
    print('else out.')              # break 없이 반복문 종료시, 출력

# 예제7
a = ['foo', 'bar', 'baz', 'qux']
s = 'qux'

i = 0

while i < len(a):
    if a[i] == s:
        break                       # baz bar foo
    i += 1
else:
    print(s, 'not found in list.')

# 무한반복
# while True:
#     print('Foo')

# 예제8
a = ['foo', 'bar', 'baz']
while True:
    if not a:
        break
    print(a.pop())


import requests

# defining the api-endpoint
API_ENDPOINT = "http://anur0narm.pythonanywhere.com/"

data = {'action': 'runner_script'}

# sending post request and saving response as response object
r = requests.post(url = API_ENDPOINT, json=data, timeout=100)

print('Site hit:\n'+str(r.text))


from urllib.parse import quote_plus
import setting

appid = setting.appID
redirect_url = setting.user_binding_page

def user_binding():
    wx_url = 'http://open.weixin.qq.com/connect/oauth2/authorize?appid={AppID}&redirect_uri={REDIRECT_URI}&response_type=code&scope={SCOPE}&state={STATE}#wechat_redirect'.format(AppID=appid, REDIRECT_URI=quote_plus(redirect_url),SCOPE='snsapi_userinfo', STATE='123')
    print('返回url: {}'.format(wx_url))
    return wx_url


def check_words_present(string1, string2):
    words1 = string1.split()
    words2 = string2.split()

    for word in words1:
        if word not in words2:
            return False

    return True

string1 = "APPLE iPhone 13 (Blue, 128 GB)"
string2 = "Apple iPhone 13 (128 GB) - Blue"

if check_words_present(string1, string2):
    print("All words in string1 are present in string2.")
else:
    print("Not all words in string1 are present in string2.")


import pygame as pg
import socket
import threading

# TODO: parameter
SERVER_IP='localhost'
SERVER_PORT=5000

class TrafficLight(object):
    def __init__(self, pos, angle):
        self.right_area = ((11,8),(24,25))
        self.forward_area = ((36,8), (30,25))
        self.angle = angle
        self.pos = pos
        self.case = pg.image.load("assets/trafficLight.png")
        self.lights = pg.Surface(self.case.get_size())
        self.status = {'r': 2, 'f': 2} # 1 = red, 2 = green, 3 = yellow
        self.__update__()

    def blit(self, screen):
        screen.blit(self.body, self.rect)

    def __update__(self):
        body = self.lights.copy()
        for k in self.status.keys():
            area = self.right_area if k == "r" else self.forward_area
            color = (255, 255, 0)
            if self.status[k] == 1:
                color = (0, 255, 0)
            elif self.status[k] == 2:
                color = (255, 255, 0) # yellow
            body.fill( color, rect=area)
        body.blit(self.case, (0, 0))
        body = pg.transform.rotate(body, self.angle)
        self.body,self.rect = body, body.get_rect().move(self.pos)

    def set_status(self, dir, status):
        if dir not in [ 'r', 'f'] or status < 0 or status > 2:
            raise Exception( "Invalid values for the Traffic light status: dir[%s] status[%d]" % (dir, status))
        self.status[dir] = status
        self.__update__()

class TrafficLightManager(object):
    def __init__(self, id=1000, host='127.0.0.1', port=7001):

        self.host = host
        self.port = port
        self.id = id
        self.running = True
        self.connected = False
        self.last_ping = 0

        self.traffic_lights = [
            TrafficLight((343, 123), 0),
            TrafficLight((479, 146), 90),
            TrafficLight((116, 266), -90),
            TrafficLight((222, 317), 180)
        ]

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # UDP
        self.sock.bind((self.host, self.port))
        self.message_thread = threading.Thread(target=self.process_message, args=())
        self.message_thread.start()

    def send_message(self, msg):
        self.sock.sendto( msg.encode('utf-8'), (SERVER_IP, SERVER_PORT))

    def kill(self):
        self.running = False
        self.sock.close()
        self.message_thread.join()

    def update(self, screen):
        for t in self.traffic_lights:
            t.blit(screen)

        if not self.connected and pg.time.get_ticks() - self.last_ping > 2000:
            print("pinging server..")
            self.send_message("i;%s;%d;%d" % (self.host, self.port, self.id))
            self.last_ping = pg.time.get_ticks()
            return

    def process_message(self):
        while self.running:
            try:
                data, addr = self.sock.recvfrom(1024)
                #print("received message: %s %s" % (data, addr))
                data = data.decode('utf-8')
                tokens = data.split(';')
                if tokens[0] == 'r':
                    self.connected = True
                    self.send_message("l;minibots;aws@123;%d" % self.id)
                elif tokens[0] == 't':  # traffic lights status
                    tf_id = 0
                    for i in range(8):
                        op = 'f' if i%2 == 0 else 'r'
                        self.traffic_lights[tf_id].set_status(op, int(tokens[i+1]))
                        if i%2 == 1: tf_id += 1

            except OSError as e:
                if e.errno != 9:
                    print(e)


import cv2

capture = cv2.VideoCapture(0)   # 괄호 안의 숫자는 장치 번호(첫번째 장치:0, 두번째:1, ...)

capture.set(3, 640)   # cv2.CAP_PROP_FRAME_WIDTH
capture.set(4, 480)   # cv2.CAP_PROP_FRAME_HEIGHT
size = (int(capture.get(3)), int(capture.get(4)))
# width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
# height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
# fps = capture.get(cv2.CAP_PROP_FPS)
fps = int(capture.get(5))   # cv2.CAP_PROP_FPS
print("fps:", fps)
print("width, height:", size)

# fourcc는 코덱 이름,
fourcc = cv2.VideoWriter_fourcc(*'XVID')    # XVID 코덱 사용 : (*'XVID')는 ('X', 'V', 'I', 'D')와 동일한 의미
# fourcc = cv2.VideoWriter_fourcc(*'DX50')    # DX50 코덱 사용
# fourcc = cv2.VideoWriter_fourcc(*'H260')    # H260 코덱 사용(코덱이 설치되지 않은 경우 에러가 발생함) => 설치 해야 함
writer = cv2.VideoWriter('capture.avi', fourcc, fps, size)

# fourcc = cv2.VideoWriter_fourcc(*'mp4v')      # lower case
# writer = cv2.VideoWriter('capture.mp4', fourcc, fps, (width, height))

cv2.namedWindow('frame', cv2.WINDOW_NORMAL)

while True:
    _, frame = capture.read()
    frame = cv2.flip(frame, 1)
    writer.write(frame)
    cv2.imshow('frame', frame)
    if cv2.waitKey(1) & 0xff == 27: break   # waitKey(1)은 최소 1ms를 대기한다는 의미(1ms 이상)

capture.release()
writer.release()


# flake8: noqa
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# patch for https://bugzilla.mozilla.org/show_bug.cgi?id=1655869
# see https://github.com/HDE/arsenic/issues/85
from arsenic.connection import *

@ensure_task
async def request(self, *, url: str, method: str, data=None) -> Tuple[int, Any]:
    if data is None:
        data = {}
    if method not in {"POST", "PUT"}:
        data = None
        headers = {}
    else:
        headers = {"Content-Type": "application/json"}
    body = json.dumps(data) if data is not None else None
    full_url = self.prefix + url
    log.info(
        "request", url=strip_auth(full_url), method=method, body=body, headers=headers
    )

    async with self.session.request(
        url=full_url, method=method, data=body, headers=headers
    ) as response:
        response_body = await response.read()
        try:
            data = json.loads(response_body)
        except JSONDecodeError as exc:
            log.error("json-decode", body=response_body)
            data = {"error": "!internal", "message": str(exc), "stacktrace": ""}
        wrap_screen(data)
        log.info(
            "response",
            url=strip_auth(full_url),
            method=method,
            body=body,
            response=response,
            data=data,
        )
        return response.status, data

Connection.request = request


#Ejercicio 1

def pertenece1 (s: list[int], e: int) -> bool:
    res = False
    i = 0
    while (i < len(s)):
        if s[i] == e:
            res = True
        i += 1
    return res

def pertenece2 (s: list[int], e: int) -> bool:
    for i in range(len(s)):
        if s[i] == e:
            return True
    return False

#Ejercicio 2

def divideATodos(s: list[int], e: int) -> bool:
    res = True
    i = 0
    if s[i] % e != 0 and i <= len(s):
        res = False
        i += 1
    return res

#Ejercicio 3

def sumaTotal(s: list[int]) -> int:
    suma = 0
    for i in range(len(s)):
        suma += s[i]
    return suma

#Ejercicio 4

def ordenados(s: list[int]) -> bool:
    for i in range(1,len(s)):
        if s[i-1] >= s[i]:
            return False
    return True

#Ejercicio 5

def longPalabra(l: list[str]) -> bool:
    for i in range(len(l)):
        if len(l[i]) > 7:
            return True
    return False

#¿y si quiero que todas las palabras sean de longitud superior a 7?
def longPalabra1(l: list[str]) -> bool:
    for i in range(len(l)):
        if len(l[i]) <= 7:
            return False
    return True

#Ejercicio 6

def esPalindroma(s: str) -> bool:
    i = 0
    while i < int(len(s)/2):
        if s[i] == s[-i-1]:
            return True
        i += 1
    return False

def tieneMinus(s: str) -> bool:
    for i in range(len(s)):
        if s[i].islower():
            return True
    return False

def tieneMayus(s: str) -> bool:
    for i in range(len(s)):
        if s[i].isupper():
            return True
    return False

def esNumero(n: str) -> bool:
    numeros = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
    if n in numeros:
        return True
    return False

def tieneNum(s: str) -> bool:
    for i in range(len(s)):
        if esNumero(s[i]):
            return True
    return False

def esContraseñaSegura(s: str) -> str:
    if len(s) > 8:
        if tieneMinus(s) and tieneMayus(s) and tieneNum(s):
            return "VERDE"
    if len(s) < 5:
        return "ROJA"
    else:
        return "AMARILLA"

#Ejercicio 8

def saldoActual(l: list[(str,int)]) -> int:
    saldo = 0
    for i in range(len(l)):
        if l[i][0] == "I":
            saldo += l[i][1]
        if l[i][0] == "R":
            saldo -= l[i][1]
    return saldo

#Ejercicio 9

def pertenece(s: list(), e) -> bool:
    for i in range(len(s)):
        if s[i] == e:
            return True
    return False

vocales = ["a", "e", "i", "o", "u"]

def numeroDeVocalesDistintas(s:str) -> int:
    apariciones = []
    contador = 0
    for i in range(len(s)):
        if pertenece(vocales, s[i]) and not pertenece(apariciones,s[i]):
            apariciones.append(s[i])
            contador += 1
    return contador

def vocalesDistintas(s: str) -> bool:
    if numeroDeVocalesDistintas(s) == 3:
        return True
    else:
        return False

#SEGUNDA PARTE

#Ejercicio 1

def cambiaPosicionesPares(l: list[int]) -> list[int]:
    i = 0
    while i < len(l):
        if esPar(i):
            l.insert(i,0)
            l.pop(i+1)
        i += 1
    return l

def esPar(num: int) -> bool:
    if num % 2 == 0:
        return True
    else:
        return False

#Ejercicio 2

def cambiaPosicionesPares2(l: list[int]) -> list[int]:
    i = 0
    l1 = []
    while i < len(l):
        if esPar(i):
            l1.append(0)
        else:
            l1.append(l[i])
        i += 1
    return l1

#Ejercicio 3

def sinVocales(s: str) -> str:
    nuevaS = s
    for i in range(len(s)):
        if pertenece(vocales, s[i]):
            nuevaS = nuevaS.replace(s[i], "")
    return nuevaS

#Ejercicio 4

def reemplazaVocales(s: str) -> str:
    nuevaS = s
    for i in range(len(s)):
        if pertenece(vocales, s[i]):
            nuevaS = nuevaS.replace(s[i], " ")
    return nuevaS

#Ejercicio 5

def daVueltaStr(s: str) -> str:
    nuevaS = ""
    for i in range(1,len(s)+1):
        nuevaS = nuevaS + s[-i]
    return nuevaS

#Ejercicio 6

def perteneceAcadaUno(l: list(list[int]), elem: int) -> list[bool]:
    res = []
    for i in range(len(l)):
        if pertenece(l[i], elem):
            res.append(True)
        else:
            res.append(False)
    return res

#Ejercicio 7

def esMatriz(m: list[list[int]]) -> bool:
    if len(m) == 0 | len(m[0]) == 0:
        return False
    for i in range(len(m)):
        if len(m[0]) != len(m[i]):
             return False
    return True

def ordenados(s: list[int]) -> bool:
    for i in range(1,len(s)):
        if s[i-1] >= s[i]:
            return False
    return True

#Ejercicio 8

def filasOrdenadas(m: list[list[int]]) -> list[bool]:
    i = 0
    res = []
    while i < len(m):
        if ordenados(m[i]):
            res.append(True)
        else:
            res.append(False)
        i += 1
    return res


'''
描述
给定一个无重复元素的整数数组nums，请你找出其中没有出现的最小的正整数

进阶： 空间复杂度 O(1)O(1)，时间复杂度 O(n)O(n)

数据范围:
-2^{31}\le nums[i] \le 2^{31}-1−2
31
 ≤nums[i]≤2
31
 −1
0\le len(nums)\le5*10^50≤len(nums)≤5∗10
5

示例1
输入：
[1,0,2]
复制
返回值：
3
复制
示例2
输入：
[-2,3,4,1,5]
复制
返回值：
2
复制
示例3
输入：
[4,5,6,8,9]
复制
返回值：
1
'''

from typing import List

#
# 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可
#
#
# @param nums int整型一维数组
# @return int整型
#
class Solution:

    def minNumberDisappeared(self, nums: List[int]) -> int:
        # write code here

        nums.sort()
        for i in range(len(nums)):
            if nums[i] > 0: break
        cnt = 1
        for j in range(i, len(nums)):
            if nums[j] != cnt: break
            cnt += 1
        return cnt

牛客网/BM53 缺失的第一个正整数.py

BM53 缺失的第一个正整数.py


def change_switch(switches, gender, num):
    if gender == 1:                                 # 남학생이면
        for j in range(num, num_switches + 1, num): # 받은 번호의 배수 번호들을
            switches[j] = 1 - switches[j]   # 반대로 바꾸기

    elif gender == 2:                               # 여학생이면
        switches[num] = 1 - switches[num]   # 받은 번호는 일단 바꾸고
        k = 1                                       # 1칸부터 시작해서
        # 왼쪽과 오른쪽 경계를 넘어가지 않도록 범위 설정, 기준 번호에서 왼쪽으로 k번째와 오른쪽으로 k번째가 같다면
        while k < min(num, num_switches - num + 1) and switches[num - k] == switches[num + k]:
            # 왼쪽 k번째와 오른쪽 k번째를 바꾸기
            switches[num - k] = 1 - switches[num - k]
            switches[num + k] = 1 - switches[num + k]
            k += 1

    return switches

# 입력 받기
num_switches = int(input())
switches = [0] + list(map(int, input().split()))
num_students = int(input())
# 성별과 번호 입력을 받아서 정의한 함수를 이용해 처리
for _ in range(num_students):
    gender, num = map(int, input().split())
    switches = change_switch(switches, gender, num)

# 첫 칸 제외하고 한줄에 20개씩 출력
switches = switches[1:]
for i in range(num_switches//20 + 1):
    print(*switches[(i*20):(i+1)*20])

백준/Silver/1244. 스위치 켜고 끄기/스위치 켜고 끄기.py

스위치 켜고 끄기.py


import requests
import simplejson as json

class FbxOSException(Exception):

    """ Exception for FreeboxOS domain """

    def __init__(self, reason):
        self.reason = reason

    def __str__(self):
        return self.reason

class FreeboxOSCtrlStorage:
    def __init__(self, controler, fbxAddress, log):
        self.controler = controler
        self.log = log
        self.fbxAddress = fbxAddress
        self.sessionToken = None

    def setSessionToken(sessionToken):
        self.sessionToken = sessionToken

    def list_disk(self):
        """ List disk """
        self.log(">>> list_disk")
        self.controler._login()
        self.log("self.controler.sessionToken=%s" % self.controler.sessionToken)
        self.log("self.sessionToken=%s" % self.sessionToken)
        # GET Launch hdd check
        headers = {
            'X-Fbx-App-Auth': self.controler.sessionToken, 'Accept': 'text/plain'}

        url = self.fbxAddress + "/api/v3/storage/disk/"
        # GET
        self.log("GET url: %s" % url)
        r = requests.get(url, headers=headers, timeout=1)
        self.log("GET response: %s" % r.text)

        # ensure status_code is 200, else raise exception
        if requests.codes.ok != r.status_code:
            raise FbxOSException("Get error: %s" % r.text)
        # rc is 200 but did we really succeed?
        resp = json.loads(r.text)
        self.log("Obj resp: %s" % resp)

        #use response
        isOn = True
        if True == resp['success']:
            for disk in resp['result']:
                print("* diskId %s " % (disk['id']) )
                for part in disk['partitions']:
                    print("  - partId %s (%s)" % (part['id'],part['label']) )
                self.log(json.dumps(disk, sort_keys=True, indent=4 * ' '))
        else:
            raise FbxOSException("Challenge failure: %s" % resp)
        self.controler._logout()
        return isOn

    def list_partition(self):
        """ List disk """
        self.log(">>> list_partition")
        self.controler._login()
        self.log("self.controler.sessionToken=%s" % self.controler.sessionToken)
        self.log("self.sessionToken=%s" % self.sessionToken)
        # GET Launch hdd check
        headers = {
            'X-Fbx-App-Auth': self.controler.sessionToken, 'Accept': 'text/plain'}

        url = self.fbxAddress + "/api/v3/storage/partition/"
        # GET
        self.log("GET url: %s" % url)
        r = requests.get(url, headers=headers, timeout=1)
        self.log("GET response: %s" % r.text)

        # ensure status_code is 200, else raise exception
        if requests.codes.ok != r.status_code:
            raise FbxOSException("Get error: %s" % r.text)
        # rc is 200 but did we really succeed?
        resp = json.loads(r.text)
        self.log("Obj resp: %s" % resp)

        #use response
        isOn = True
        if True == resp['success']:
            for part in resp['result']:
                print("* partId %s (%s)" % (part['id'],part['label']) )
                print("  - diskId %s" % (part['disk_id']) )
                self.log(json.dumps(part, sort_keys=True, indent=4 * ' '))
        else:
            raise FbxOSException("Challenge failure: %s" % resp)
        self.controler._logout()
        return isOn

    def _launchDiskCheckOn(self,partitionId,headers):
        """ launch check on one partition """
        self.log(">>> _launchDiskCheckOn %s " % partitionId)

        url = self.fbxAddress + "/api/v3/storage/partition/"+str(partitionId)+"/check"
        parameter = {'checkmode': 'ro'}
        # GET
        self.log("PUT url: %s" % url)

        r = requests.put(url, headers=headers, data=json.dumps(parameter), timeout=1)
        self.log("PUT response: %s" % r.text)

        # ensure status_code is 200, else raise exception
        if requests.codes.ok != r.status_code:
            raise FbxOSException("Get error: %s" % r.text)
        # rc is 200 but did we really succeed?

        resp = json.loads(r.text)
        self.log("Obj resp: %s" % resp)

        #use response
        isLaunch = False
        if True == resp['success']:
            isLaunch = True
        else:
            raise FbxOSException("Challenge failure: %s" % resp)

        return isLaunch

    def _checkDiskState(self,partitionId,headers):
        """ launch check of  disk on all disks"""
        self.log(">>> _checkDiskState")
        url = self.fbxAddress + "/api/v3/storage/partition/" + str(partitionId)

        # GET
        self.log("GET url: %s" % url)
        r = requests.get(url, headers=headers, timeout=1)
        self.log("GET response: %s" % r.text)

        # ensure status_code is 200, else raise exception
        if requests.codes.ok != r.status_code:
            raise FbxOSException("Get error: %s" % r.text)
        # rc is 200 but did we really succeed?

        resp = json.loads(r.text)
        self.log("Obj resp: %s" % resp)

        #use response
        isStateCheck = False
        if True == resp['success']:
            state = resp['result']['state']
            self.log("Partion id %s in state : %s" % (partitionId,state))
            isStateCheck = (state == 'checking')
            self.log('isStateCheck %s' % isStateCheck)

        else:
            raise FbxOSException("Challenge failure: %s" % resp)
        return isStateCheck

    def isCheckAllowed(self,diskId):
        """ test if check of disk is possible"""
        self.log(">>> isCheckAllowed %s" % diskId)

        isAllow = False
        self.controler._login()
        # GET Launch hdd check
        headers = {
            'X-Fbx-App-Auth': self.controler.sessionToken, 'Accept': 'text/plain'}
        url = self.fbxAddress + "/api/v3/storage/disk/%s" % diskId
        # GET
        self.log("GET url: %s" % url)
        r = requests.get(url, headers=headers, timeout=1)
        self.log("GET response: %s" % r.text)

        # ensure status_code is 200, else raise exception
        if requests.codes.ok != r.status_code:
            raise FbxOSException("Get error: %s" % r.text)
        # rc is 200 but did we really succeed?

        resp = json.loads(r.text)
        self.log("Obj resp: %s" % resp)

        #use response
        isOn = True
        if True == resp['success']:
            isAllow = resp['result']['idle']
        return isAllow

    def check_partition_all(self):
        """ launch check of  disk on all disks"""
        self.log(">>> check_partition_all")

        self.controler._login()
        # GET Launch hdd check
        headers = {
            'X-Fbx-App-Auth': self.controler.sessionToken, 'Accept': 'text/plain'}

        url = self.fbxAddress + "/api/v3/storage/partition/"

        # GET
        self.log("GET url: %s" % url)
        r = requests.get(url, headers=headers, timeout=1)
        self.log("GET response: %s" % r.text)

        # ensure status_code is 200, else raise exception
        if requests.codes.ok != r.status_code:
            raise FbxOSException("Get error: %s" % r.text)
        # rc is 200 but did we really succeed?

        resp = json.loads(r.text)
        self.log("Obj resp: %s" % resp)

        #use response
        isOn = True
        if True == resp['success']:
            for part in resp['result']:
                launched = False
                self.log(json.dumps(part, sort_keys=True, indent=4 * ' '))
                partId=part['id']
                diskId=part['disk_id']
                statusDisk=""
                if (self.isCheckAllowed(diskId)):
                    launched = True
                    launched=self._launchDiskCheckOn(partId,headers)
                    if (launched):
                        launched = self._checkDiskState(partId,headers)
                        self.log("disk checking %s(check)" % launched)
                    statusDisk="" if launched else " not"
                else:
                    statusDisk=" not Possible"
                print("Partition Check%s Launch on Disk%s (partition %s)" % (statusDisk,part['disk_id'],partId))
        else:
            raise FbxOSException("Challenge failure: %s" % resp)
        self.controler._logout()
        return isOn


import numpy as np
from sys import stdin
import copy
###############################
graph=[]
vertice_num=0
delta=0
checked_color=[]

file = stdin
# file = open("input.txt")
data=file.readlines()

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

def Allowed_colors(index):
    temp=[]

    for i in range(1,delta+2):
        if i in graph[index]:
            pass
        else:
            temp.append(i)

    return copy.deepcopy(temp)

###############################
def change_color_path(v,color1,color2):

    for i in range(vertice_num):
        if graph[v][i] == color1:
            graph[v][i]=color2
            graph[i][v]=color2
            change_color_path(i,color2,color1)

###############################
def coloring(V1,V2):

    V1_color=np.array(Allowed_colors(V1))
    V2_color=np.array(Allowed_colors(V2))
    intersect=np.intersect1d(V1_color,V2_color)

    if len(intersect)!=0:
        graph[V1][V2]=intersect[0]
        graph[V2][V1]=intersect[0]

    else:
        if V2_color[0] in checked_color:
            change_color_path(V2,V2_color[0],V1_color[0])

        else:
            checked_color.append(V2_color[0])
            V3=0
            for i in range(vertice_num):
                if graph[V1][i] == V2_color[0]:
                    V3=i
                    break

            coloring(V1,V3)

        V1_color=np.array(Allowed_colors(V1))
        V2_color=np.array(Allowed_colors(V2))
        intersect=np.intersect1d(V1_color,V2_color)

###############################
def print_results():
    global data
    global delta

    color_set=set()

    for i in range(vertice_num):
        for j in range(vertice_num):
            color_set.add(graph[i][j])

    color_set.remove(-1)

    print(delta," ",len(color_set))

    for d in range(len(data)):

        if d!=0:
            node_data=data[d].split()
            print(node_data[0]," ",node_data[1]," ",graph[int(node_data[0])][int(node_data[1])])

###############################
def read_file():
    global data
    global delta
    global vertice_num

    for d in range(len(data)):

        node_data=data[d].split()

        if d==0:
            vertice_num=int(node_data[0])
            temp_list=[]

            for j in range(vertice_num):
                temp_list.append(-1)

            for i in range(vertice_num):
                graph.append(copy.deepcopy(temp_list))

        else:
            graph[int(node_data[0])][int(node_data[1])]=0
            graph[int(node_data[1])][int(node_data[0])]=0

    for i in range(vertice_num):
        m=graph[i].count(0)
        if m > delta:
            delta=m

###############################
def main():
    global checked_color

    read_file()

    for i in range(vertice_num):
        for j in range(vertice_num):
            if graph[i][j]==0:
                coloring(i,j)
                checked_color=[]

    print_results()

###############################
if __name__ == "__main__":
    main()


from os.path import exists
from os import remove, makedirs
from .types import Files, FilePath

def get_file_output_folder(file: FilePath) -> FilePath:
    return file.rsplit("/", 1)[0]

def get_write_paths(files: Files) -> Files:
    return list(
        (file.replace("resources", "build"), content) for file, content in files
    )

def write_files(files: Files) -> None:
    write_files = get_write_paths(files)

    # Remove old build, if present.
    for destination, _ in write_files:
        if exists(destination):
            remove(destination)

    for file_path, content in write_files:
        folder_path = get_file_output_folder(file_path)
        if not exists(folder_path):
            makedirs(folder_path)

        file = open(file_path, "w")
        file.write(content)
        file.close()


import logging
import os
import signal
import sys
import time
from random import random, shuffle
from tempfile import mkstemp

from eventlet import spawn, patcher, Timeout

import swift.common.db
from swift.container.server import DATADIR
from swift.common.bufferedhttp import http_connect
from swift.common.db import ContainerBroker
from swift.common.exceptions import ConnectionTimeout
from swift.common.ring import Ring
from swift.common.utils import get_logger, config_true_value, dump_recon_cache
from swift.common.daemon import Daemon
from swift.common.http import is_success, HTTP_INTERNAL_SERVER_ERROR

class ContainerUpdater(Daemon):
    """Update container information in account listings."""

    def __init__(self, conf):
        self.conf = conf
        self.logger = get_logger(conf, log_route='container-updater')
        self.devices = conf.get('devices', '/srv/node')
        self.mount_check = config_true_value(conf.get('mount_check', 'true'))
        self.swift_dir = conf.get('swift_dir', '/etc/swift')
        self.interval = int(conf.get('interval', 300))
        self.account_ring = None
        self.concurrency = int(conf.get('concurrency', 4))
        self.slowdown = float(conf.get('slowdown', 0.01))
        self.node_timeout = int(conf.get('node_timeout', 3))
        self.conn_timeout = float(conf.get('conn_timeout', 0.5))
        self.no_changes = 0
        self.successes = 0
        self.failures = 0
        self.account_suppressions = {}
        self.account_suppression_time = \
            float(conf.get('account_suppression_time', 60))
        self.new_account_suppressions = None
        swift.common.db.DB_PREALLOCATION = \
            config_true_value(conf.get('db_preallocation', 'f'))
        self.recon_cache_path = conf.get('recon_cache_path',
                                         '/var/cache/swift')
        self.rcache = os.path.join(self.recon_cache_path, "container.recon")

    def get_account_ring(self):
        """Get the account ring.  Load it if it hasn't been yet."""
        if not self.account_ring:
            self.account_ring = Ring(self.swift_dir, ring_name='account')
        return self.account_ring

    def get_paths(self):
        """
        Get paths to all of the partitions on each drive to be processed.

        :returns: a list of paths
        """
        paths = []
        for device in os.listdir(self.devices):
            dev_path = os.path.join(self.devices, device)
            if self.mount_check and not os.path.ismount(dev_path):
                self.logger.warn(_('%s is not mounted'), device)
                continue
            con_path = os.path.join(dev_path, DATADIR)
            if not os.path.exists(con_path):
                continue
            for partition in os.listdir(con_path):
                paths.append(os.path.join(con_path, partition))
        shuffle(paths)
        return paths

    def _load_suppressions(self, filename):
        try:
            with open(filename, 'r') as tmpfile:
                for line in tmpfile:
                    account, until = line.split()
                    until = float(until)
                    self.account_suppressions[account] = until
        except Exception:
            self.logger.exception(
                _('ERROR with loading suppressions from %s: ') % filename)
        finally:
            os.unlink(filename)

    def run_forever(self, *args, **kwargs):
        """
        Run the updator continuously.
        """
        time.sleep(random() * self.interval)
        while True:
            self.logger.info(_('Begin container update sweep'))
            begin = time.time()
            now = time.time()
            expired_suppressions = \
                [a for a, u in self.account_suppressions.iteritems()
                 if u < now]
            for account in expired_suppressions:
                del self.account_suppressions[account]
            pid2filename = {}
            # read from account ring to ensure it's fresh
            self.get_account_ring().get_nodes('')
            for path in self.get_paths():
                while len(pid2filename) >= self.concurrency:
                    pid = os.wait()[0]
                    try:
                        self._load_suppressions(pid2filename[pid])
                    finally:
                        del pid2filename[pid]
                fd, tmpfilename = mkstemp()
                os.close(fd)
                pid = os.fork()
                if pid:
                    pid2filename[pid] = tmpfilename
                else:
                    signal.signal(signal.SIGTERM, signal.SIG_DFL)
                    patcher.monkey_patch(all=False, socket=True)
                    self.no_changes = 0
                    self.successes = 0
                    self.failures = 0
                    self.new_account_suppressions = open(tmpfilename, 'w')
                    forkbegin = time.time()
                    self.container_sweep(path)
                    elapsed = time.time() - forkbegin
                    self.logger.debug(
                        _('Container update sweep of %(path)s completed: '
                          '%(elapsed).02fs, %(success)s successes, %(fail)s '
                          'failures, %(no_change)s with no changes'),
                        {'path': path, 'elapsed': elapsed,
                         'success': self.successes, 'fail': self.failures,
                         'no_change': self.no_changes})
                    sys.exit()
            while pid2filename:
                pid = os.wait()[0]
                try:
                    self._load_suppressions(pid2filename[pid])
                finally:
                    del pid2filename[pid]
            elapsed = time.time() - begin
            self.logger.info(_('Container update sweep completed: %.02fs'),
                             elapsed)
            dump_recon_cache({'container_updater_sweep': elapsed},
                             self.rcache, self.logger)
            if elapsed < self.interval:
                time.sleep(self.interval - elapsed)

    def run_once(self, *args, **kwargs):
        """
        Run the updater once.
        """
        patcher.monkey_patch(all=False, socket=True)
        self.logger.info(_('Begin container update single threaded sweep'))
        begin = time.time()
        self.no_changes = 0
        self.successes = 0
        self.failures = 0
        for path in self.get_paths():
            self.container_sweep(path)
        elapsed = time.time() - begin
        self.logger.info(_(
            'Container update single threaded sweep completed: '
            '%(elapsed).02fs, %(success)s successes, %(fail)s failures, '
            '%(no_change)s with no changes'),
            {'elapsed': elapsed, 'success': self.successes,
             'fail': self.failures, 'no_change': self.no_changes})
        dump_recon_cache({'container_updater_sweep': elapsed},
                         self.rcache, self.logger)

    def container_sweep(self, path):
        """
        Walk the path looking for container DBs and process them.

        :param path: path to walk
        """
        for root, dirs, files in os.walk(path):
            for file in files:
                if file.endswith('.db'):
                    self.process_container(os.path.join(root, file))
                    time.sleep(self.slowdown)

    def process_container(self, dbfile):
        """
        Process a container, and update the information in the account.

        :param dbfile: container DB to process
        """
        start_time = time.time()
        broker = ContainerBroker(dbfile, logger=self.logger)
        info = broker.get_info()
        # Don't send updates if the container was auto-created since it
        # definitely doesn't have up to date statistics.
        if float(info['put_timestamp']) <= 0:
            return
        if self.account_suppressions.get(info['account'], 0) > time.time():
            return
        if info['put_timestamp'] > info['reported_put_timestamp'] or \
                info['delete_timestamp'] > info['reported_delete_timestamp'] \
                or info['object_count'] != info['reported_object_count'] or \
                info['bytes_used'] != info['reported_bytes_used']:
            container = '/%s/%s' % (info['account'], info['container'])
            part, nodes = self.get_account_ring().get_nodes(info['account'])
            events = [spawn(self.container_report, node, part, container,
                            info['put_timestamp'], info['delete_timestamp'],
                            info['object_count'], info['bytes_used'])
                      for node in nodes]
            successes = 0
            failures = 0
            for event in events:
                if is_success(event.wait()):
                    successes += 1
                else:
                    failures += 1
            if successes > failures:
                self.logger.increment('successes')
                self.successes += 1
                self.logger.debug(
                    _('Update report sent for %(container)s %(dbfile)s'),
                    {'container': container, 'dbfile': dbfile})
                broker.reported(info['put_timestamp'],
                                info['delete_timestamp'], info['object_count'],
                                info['bytes_used'])
            else:
                self.logger.increment('failures')
                self.failures += 1
                self.logger.debug(
                    _('Update report failed for %(container)s %(dbfile)s'),
                    {'container': container, 'dbfile': dbfile})
                self.account_suppressions[info['account']] = until = \
                    time.time() + self.account_suppression_time
                if self.new_account_suppressions:
                    print >>self.new_account_suppressions, \
                        info['account'], until
            # Only track timing data for attempted updates:
            self.logger.timing_since('timing', start_time)
        else:
            self.logger.increment('no_changes')
            self.no_changes += 1

    def container_report(self, node, part, container, put_timestamp,
                         delete_timestamp, count, bytes):
        """
        Report container info to an account server.

        :param node: node dictionary from the account ring
        :param part: partition the account is on
        :param container: container name
        :param put_timestamp: put timestamp
        :param delete_timestamp: delete timestamp
        :param count: object count in the container
        :param bytes: bytes used in the container
        """
        with ConnectionTimeout(self.conn_timeout):
            try:
                conn = http_connect(
                    node['ip'], node['port'], node['device'], part,
                    'PUT', container,
                    headers={'X-Put-Timestamp': put_timestamp,
                             'X-Delete-Timestamp': delete_timestamp,
                             'X-Object-Count': count,
                             'X-Bytes-Used': bytes,
                             'X-Account-Override-Deleted': 'yes'})
            except (Exception, Timeout):
                self.logger.exception(_(
                    'ERROR account update failed with '
                    '%(ip)s:%(port)s/%(device)s (will retry later): '), node)
                return HTTP_INTERNAL_SERVER_ERROR
        with Timeout(self.node_timeout):
            try:
                resp = conn.getresponse()
                resp.read()
                return resp.status
            except (Exception, Timeout):
                if self.logger.getEffectiveLevel() <= logging.DEBUG:
                    self.logger.exception(
                        _('Exception with %(ip)s:%(port)s/%(device)s'), node)
                return HTTP_INTERNAL_SERVER_ERROR


import pandas as pd

# Step 1: Read the Excel file
file_path = "employee_data.xlsx"
df = pd.read_excel(file_path, engine='openpyxl')

# Step 2: Sort the dataframe by employee and date
df.sort_values(by=['Employee Name', 'Date'], inplace=True)

# Step 3: Find employees who have less than 10 hours between shifts but greater than 1 hour
shift_gap_threshold_min = 60  # 1 hour in minutes
max_shift_gap_min = 600  # 10 hours in minutes
shift_gap_employees = []

for employee, employee_df in df.groupby('Employee Name'):
    last_shift_end = None

    for _, row in employee_df.iterrows():
        if last_shift_end is not None:
            time_between_shifts_min = (row['Date'] - last_shift_end).total_seconds() / 60
            if shift_gap_threshold_min < time_between_shifts_min < max_shift_gap_min:
                shift_gap_employees.append((employee, row['Position']))
        last_shift_end = row['Date']

# Step 4: Print the names and positions of employees who meet the criteria
if shift_gap_employees:
    print("Employees with less than 10 hours between shifts but greater than 1 hour:")
    for employee, position in shift_gap_employees:
        print(f"Name: {employee}, Position: {position}")
else:
    print("No employees meet the criteria.")


from scrapy import Request,Spider
from ..items import _1919Item
from pymongo import MongoClient
from scrapy.exceptions import CloseSpider
import logging

logger = logging.getLogger(__name__)

class DetailSpider(Spider):
    name = 'detail_spider'
    start_urls = []

    def __init__(self):
        conn = MongoClient(host='114.112.107.58', port=27017)
        db = conn['_1919']
        self.col = db['detail_urls']

    def start_requests(self):
        cursor = self.col.find({},no_cursor_timeout=True).batch_size(10)
        for data in cursor:
            if data:
                yield Request(url=data['detail_url'],callback=self.parse_detail)

    def parse_detail(self, response):
        if not response.status == 200:
            logger.info("http error!!!")
            raise CloseSpider
        if response.status == 400:
            logger.info("抓取失败！！！")
            raise CloseSpider
        item = _1919Item()
        item['price'] = response.xpath('//em[@class="details-pri"]/text()').extract_first(default=None)
        item['name'] = response.xpath('//div[@class="pcc-title"]/h1/text()').extract_first().replace('\t','')
        shop = {}
        shop['name'] = response.xpath('//div[@class="product-cr-title"]/h1/text()').extract_first().replace('""','')
        shop['url'] = response.xpath('//div[@class="product-cr-adLogo"]/a/@href').extract_first()
        if shop:
            item['shop'] = shop
        else:
            item['shop'] = {}
        category = response.xpath('//div[@class="dGuide"]//text()').extract()
        category = ''.join(category)
        if category:
            item['category'] = category.replace('\n', '').replace('\t', '').replace(' ', '')
        item['source_url'] = response.url
        images = []
        datas = response.xpath('//div[@id="imageMenu"]/ul/li')
        for data in datas:
            img_json = {}
            img_json['url'] = data.xpath('img/@src').extract_first()
            img_json['md5'] = '********************************'
            images.append(img_json)
        item['images'] = images
        detail_list = []
        for data in response.xpath('//div[@class="intro-cont com-size"]'):
            str = data.xpath('span//text()').extract()
            detail = {}
            detail['key'] = str[0].replace('：', '')
            detail['value'] = str[-1]
            detail_list.append(detail)
        if detail_list:
            item['detail'] = detail_list
        else:
            item['detail'] = []
        print(item)
        yield item


from menu import Menu, MenuItem
from coffee_maker import CoffeeMaker
from money_machine import MoneyMachine

menu = Menu()
money_machine = MoneyMachine()
coffee_maker = CoffeeMaker()

working_state = True

while working_state:
    user_input = input("What would you like? (espresso/latte/cappuccino): ")
    if user_input == "report":
        money_machine.report()
        coffee_maker.report()
    else:
        menu_item = menu.find_drink(user_input)
        if menu_item and coffee_maker.is_resource_sufficient(menu_item) and money_machine.make_payment(menu_item.cost):
            coffee_maker.make_coffee(menu_item)


from django.contrib import admin, messages
from django.http import HttpResponse
from django.utils.text import slugify

from .tito import export_participants_to_csv
from .models import Batch, Participant

@admin.register(Participant)
class ParticipantAdmin(admin.ModelAdmin):
    list_display = ['pk', 'first_name', 'last_name', 'email', 'ticket_type', 'batch', 'created_at']
    list_display_links = ['pk', 'first_name', 'last_name', 'email']
    list_filter = ['ticket_type', 'batch']
    search_fields = ['first_name', 'last_name', 'email']
    readonly_fields = ['created_at']

@admin.register(Batch)
class BatchAdmin(admin.ModelAdmin):
    list_display = ['pk', 'name', 'tickets', 'assigned_at', 'created_at']
    list_display_links = ['pk', 'name']
    readonly_fields = ['created_at', 'assigned_at']
    actions = ['assign_participants', 'export_to_csv']

    def assign_participants(self, request, queryset):
        if len(queryset) != 1 or queryset[0].assigned:
            msg = 'Action available only for one, unassigned Batch'
            self.message_user(request, msg, level=messages.ERROR)
            return

        batch = queryset[0]
        assigned_participants = batch.assign_participants()

        msg = '%d participants assigned to %s' % (assigned_participants, batch)
        level = messages.SUCCESS if assigned_participants == batch.tickets else messages.WARNING
        self.message_user(request, msg, level=level)

    def export_to_csv(self, request, queryset):
        if len(queryset) != 1 or not queryset[0].assigned:
            msg = 'Action available only for one, assigned Batch'
            self.message_user(request, msg, level=messages.ERROR)
            return

        batch = queryset[0]
        participants = batch.participants.order_by('email')
        csv = export_participants_to_csv(participants)

        response = HttpResponse(csv, content_type='text/csv')
        response['Content-Disposition'] = 'attachment; filename="%s.csv"' % slugify(batch)
        return response


from PyPDF2 import PdfReader

def get_pdf_text(pdf_files):
    if not pdf_files:
        return ""

    raw_text = ""

    for pdf_file in pdf_files:
        pdf = PdfReader(pdf_file)
        num_pages = len(pdf.pages)

        for page_num in range(num_pages):
            page = pdf.pages[page_num]
            raw_text += page.extract_text()

    return raw_text


#! /usr/bin/python3
import re

def regexStrip(string, optChars= "no"):
    whiteBeginRegex = re.compile(r'^\s*|\s*$')
    optBeginRegex = re.compile('^(' + optChars + ')*|(' + optChars + ')*$')
    mo1 = whiteBeginRegex.sub('', string)
    mo2 = optBeginRegex.sub('', string)
    if optChars == "no":
        print('2nd argument not activated')
        print(mo1)
    else:
        print('2nd argument activated')
        print(mo2)

regexStrip('                Penis           ')

End-of-chapter challenges in ATBS/Chapter 07 – Pattern Matching with Regex/Regex Version of strip().py