import torch.nn.functional as F
import torch

import numpy as np

def to_crop_resize(img, bbox, landm, resize=128):
    buff = torch.zeros(img.size(0), img.size(1), resize, resize)
    landm_buff = torch.zeros_like(landm)
    for i, (box, mark) in enumerate(zip(bbox, landm)):
        # image cropping and resizing
        x1, y1, x2, y2 = box
        im = img[i,:,y1:y2,x1:x2].unsqueeze(0)
        buff[i] = F.interpolate(im, [resize, resize], mode='bilinear', align_corners=False)

        # landmark rescaling
        h, w = im.shape[2:]
        y_scale, x_scale = resize / h, resize / w
        mark = mark.to(torch.float64)
        mark[::2] = (mark[::2]-x1) * x_scale
        mark[1::2] = (mark[1::2]-y1) * y_scale
        landm_buff[i] = mark.to(torch.int64)
    return buff, landm_buff

def to_restore_size(img, cropped, bbox):
    buff = torch.zeros_like(img)
    for i, box in enumerate(bbox):
        x1, y1, x2, y2 = box
        img[i,:,y1:y2,x1:x2] = F.interpolate(cropped[i].unsqueeze(0),
                                             [y2-y1, x2-x1], mode='bilinear', align_corners=False)[0]
    return img

def to_expand_bbox(bbox, landm, h, w, percentage=0.05):
    shrink_percentage = np.random.uniform(-percentage, percentage)

    pnts_x = landm[:,0::2]
    pnts_y = landm[:,1::2]
    max_x = pnts_x.max(1)[0]
    min_x = pnts_x.min(1)[0]
    max_y = pnts_y.max(1)[0]
    min_y = pnts_y.min(1)[0]
    minmax_xy = torch.stack([min_x, min_y, max_x, max_y])
    minmax_xy = minmax_xy.t()

    gap = (bbox - minmax_xy).to(torch.float64).to(bbox.device)

    x1 = torch.stack([bbox[:,0], min_x]).t().min(1, keepdim=True)[0]
    y1 = torch.stack([bbox[:,1], min_y]).t().min(1, keepdim=True)[0]
    x2 = torch.stack([bbox[:,2], max_x]).t().max(1, keepdim=True)[0]
    y2 = torch.stack([bbox[:,3], max_y]).t().max(1, keepdim=True)[0]
#    expanded_ = torch.cat([x1, y1, x2, y2], dim=1)+ (gap * (0.5+shrink_percentage)).to(bbox.dtype)
#    expanded_ = torch.cat([x1, y1, x2, y2], dim=1)+ (gap * 0.8).to(bbox.dtype)
    expanded_ = torch.cat([x1, y1, x2, y2], dim=1)+ (gap * 0.5).to(bbox.dtype)

    expanded_[:,0] = torch.where(expanded_[:,0] > 0, expanded_[:,0], 0*torch.ones_like(expanded_[:,0]))
    expanded_[:,1] = torch.where(expanded_[:,1] > 0, expanded_[:,1], 0*torch.ones_like(expanded_[:,1]))
    expanded_[:,2] = torch.where(expanded_[:,2] < w, expanded_[:,2], w*torch.ones_like(expanded_[:,2]))
    expanded_[:,3] = torch.where(expanded_[:,3] < h, expanded_[:,3], h*torch.ones_like(expanded_[:,3]))

    return expanded_

def to_apply_mask(img, bbox):
    """mask bbox of image"""
    for i, box in enumerate(bbox):
        x1, y1, x2, y2 = box
#        img[i][:,y1:y2,x1:x2] = 2*(128/255)-1
#        noise = np.random.normal(0.0, 0.1)
#        img[i][:,y1:y2,x1:x2] = torch.from_numpy(noise)
        img[i][:,y1:y2,x1:x2] = img[i][:,y1:y2,x1:x2].normal_(0.0, 0.1)
    return img

def to_onehot(landm, h, w):
    onehot = torch.zeros(landm.shape[0], 1, h, w)
    for i, mark in enumerate(landm):
        mark = torch.where(mark > 127, torch.tensor(127).to(mark.device), mark)
        onehot[i,0,mark[0],mark[1]] = 1
        onehot[i,0,mark[2],mark[3]] = 1
        onehot[i,0,mark[4],mark[5]] = 1
        onehot[i,0,mark[6],mark[7]] = 1
        onehot[i,0,mark[8],mark[9]] = 1
    return onehot

def softmax_(fake_cls):
    return torch.nn.functional.softmax(fake_cls.detach(), dim=1)

def accuracy_(real_cls, predicted):
    argmax = torch.argmax(predicted, dim=1)
    correct_ = torch.where(argmax==real_cls, torch.ones_like(argmax), torch.zeros_like(argmax)).to(predicted.dtype)
    return torch.sum(correct_) / len(real_cls)

# Load fewer layers of pre-trained models if possible
def load(model, cpk_file, netname=None):
    if netname is not None:
        pretrained_dict = torch.load(cpk_file)[netname]
    else:
        pretrained_dict = torch.load(cpk_file)
    model_dict = model.state_dict()
    pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}
    model_dict.update(pretrained_dict)
    model.load_state_dict(model_dict)


from __future__ import absolute_import
from __future__ import print_function

import os
from collections import namedtuple, MutableMapping
from copy import deepcopy, copy
from itertools import ifilter
import logging

import pyros_utils
import rospy
import rosservice, rostopic, rosparam

import re
import ast
import socket
import threading
import Queue

import time

# create logger
_logger = logging.getLogger(__name__)
# and let it propagate to parent logger, or other handler
# the user of pyros should configure handlers

from pyros_interfaces_common.transient_if_pool import DiffTuple
from .baseinterface import BaseInterface
from .param_if_pool import RosParamIfPool
from .service_if_pool import RosServiceIfPool
from .subscriber_if_pool import RosSubscriberIfPool
from .publisher_if_pool import RosPublisherIfPool

from .connection_cache_utils import connection_cache_proxy_create, connection_cache_marshall, connection_cache_merge_marshalled

try:
    import rocon_python_comms
except ImportError:
    rocon_python_comms = None

# To make sure things dont get messed up between threads
CacheTuple = (namedtuple("CacheTuple", "complete added removed"))

class RosInterface(BaseInterface):

    """
    RosInterface.
    """
    def __init__(self, node_name, publishers=None, subscribers=None, services=None, params=None, enable_cache=False, argv=None):
        # This runs in a child process (managed by PyrosROS) and as a normal ros node)

        # First thing to do : find the rosmaster...
        # master has to be running here or we just wait for ever
        # TODO : improve...
        m, _ = pyros_utils.get_master(spawn=False)
        while not m.is_online():
            _logger.warning("ROSMASTER not found !!!...")
            time.sleep(0.5)

        # Second thing to do : initialize the ros node and disable signals to avoid overriding callers behavior
        rospy.init_node(node_name, argv=argv, disable_signals=True)
        rospy.loginfo('RosInterface {name} node started with args : {argv}'.format(name=node_name, argv=argv))
        # note on dynamic update (config reload, etc.) this is reinitialized
        # rospy.init_node supports being reinitialized with the exact same arguments

        self.enable_cache = enable_cache
        if self.enable_cache and rocon_python_comms is None:
            rospy.logerr("Connection Cache enabled for RosInterface, but rocon_python_comms not found. Disabling.")
            self.enable_cache = False

        # only one queue to avoid sync issues
        self.cb_ss = Queue.Queue()

        # we add params from ROS environment, if we get something there (bwcompat behavior)
        services = services or []
        publishers = publishers or []
        subscribers = subscribers or []
        params = params or []
        services += list(set(ast.literal_eval(rospy.get_param('~services', "[]"))))

        # bwcompat
        publishers += list(set(ast.literal_eval(rospy.get_param('~topics', "[]"))))
        subscribers += list(set(ast.literal_eval(rospy.get_param('~topics', "[]"))))
        # new
        publishers += list(set(ast.literal_eval(rospy.get_param('~publishers', "[]"))))
        subscribers += list(set(ast.literal_eval(rospy.get_param('~subscribers', "[]"))))

        params += list(set(ast.literal_eval(rospy.get_param('~params', "[]"))))
        enable_cache = rospy.get_param('~enable_cache', enable_cache)

        if enable_cache is not None:
            self.enable_cache = enable_cache
        # Note : None means no change ( different from [] )
        rospy.loginfo("""[{name}] ROS Interface initialized with:
        -    services : {services}
        -    publishers : {publishers}
        -    subscribers : {subscribers}
        -    params : {params}
        -    enable_cache : {enable_cache}
        """.format(
            name=__name__,
            publishers="\n" + "- ".rjust(10) + "\n\t- ".join(publishers) if publishers else [],
            subscribers="\n" + "- ".rjust(10) + "\n\t- ".join(subscribers) if subscribers else [],
            services="\n" + "- ".rjust(10) + "\n\t- ".join(services) if services else [],
            params="\n" + "- ".rjust(10) + "\n\t- ".join(params) if params else [],
            enable_cache=enable_cache)
        )

        # This base constructor assumes the system to interface with is already available ( can do a get_svc_available() )
        params_pool = RosParamIfPool(params)
        services_pool = RosServiceIfPool(services)
        subscribers_pool = RosSubscriberIfPool(subscribers)
        publishers_pool = RosPublisherIfPool(publishers)

        super(RosInterface, self).__init__(publishers_pool, subscribers_pool, services_pool, params_pool)

        # connecting to the master via proxy object
        self._master = rospy.get_master()

        #: If enabled, connection cache proxy will be setup in update() to allow dynamic update via config.
        # TODO : double check : maybe useless now since we completely reinit the interface for dynamic update...
        self.connection_cache = None

        # Setup our debug log
        # We need this debug log since rospy.logdebug does NOT store debug messages in the log.
        # But it should Ref : http://wiki.ros.org/rospy/Overview/Logging#Reading_log_messages
        # TODO : find why.
        ros_home = os.environ.get('ROS_HOME', os.path.join(os.path.expanduser("~"), '.ros'))
        self._debug_logger = logging.getLogger('pyros.ros_interface')
        if not os.path.exists(os.path.join(ros_home, 'logdebug')):
            os.makedirs(os.path.join(ros_home, 'logdebug'))
        logfilename = os.path.join(ros_home, 'logdebug', rospy.get_name()[1:].replace(os.path.sep, "-") + '_pyros_rosinterface.log')
        file_handler = logging.handlers.RotatingFileHandler(
            logfilename,
            maxBytes=100 * 131072,
            backupCount=10
        )
        self._debug_logger.propagate = False  # to avoid propagating to root logger
        self._debug_logger.setLevel(logging.DEBUG)
        self._debug_logger.addHandler(file_handler)

    def retrieve_system_state(self):
        """
        This will retrieve the system state from ROS master if needed, and apply changes to local variable to keep
        a local representation of the connections available up to date.
        """
        try:
            # we call the master only if we dont get system_state from connection cache
            if self.enable_cache and self.connection_cache is not None:
                publishers, subscribers, services = self.connection_cache.getSystemState()
                topic_types = self.connection_cache.getTopicTypes()
                try:
                    service_types = self.connection_cache.getServiceTypes()
                    # handling fallback here since master doesnt have the API
                except rocon_python_comms.UnknownSystemState as exc:
                    service_types = []
            else:
                publishers, subscribers, services = self._master.getSystemState()[2]
                topic_types = self._master.getTopicTypes()[2]
                service_types = []  # master misses this API to be consistent

            # Getting this doesnt depend on cache for now
            params = set(rospy.get_param_names())

            return publishers, subscribers, services, params, topic_types, service_types

        except socket.error:
            rospy.logerr("[{name}] couldn't get system state from the master ".format(name=__name__))

    # for use with line_profiler or memory_profiler
    # Not working yet... need to solve multiprocess profiling issues...
    #@profile
    def update(self):

        backedup_complete_cb_ss = None

        #update will retrieve system state here
        publishers = []
        subscribers = []
        services = []
        params = []
        topic_types = []
        service_types = []

        # and populate these to represent the changes done on the interface
        params_if_dt = DiffTuple([], [])
        services_if_dt = DiffTuple([], [])
        subscribers_if_dt = DiffTuple([], [])
        publishers_if_dt = DiffTuple([], [])

        # Destroying connection cache proxy if needed
        if self.connection_cache is not None and not self.enable_cache:
            # removing existing connection cache proxy to force a reinit of everything
            # to make sure we dont get a messed up system state with wrong list/diff from
            # dynamically switching cache on and off.
            self.connection_cache = None

        # TODO Instead of one or the other, we should have "two layer behaviors" with different frequencies
        # Fast loop checking only diff
        # Slow loop checking full state
        # It will allow recovering from any mistakes because of wrong diffs (update speed/race conditions/etc.)
        if self.enable_cache:
            if self.connection_cache is None:  # Building Connection Cache Proxy if needed
                self.connection_cache = connection_cache_proxy_create(self._proxy_cb)
                if self.connection_cache:
                    self.enable_cache = True
                else:
                    self.enable_cache = False

        # TMP until it s implemented in the connection cache
        # Because the cache doesnt currently do it
        params = set(rospy.get_param_names())
        # determining params diff despite lack of API
        params_dt = DiffTuple(
            added=[p for p in params if p not in self.params_available],
            removed=[p for p in self.params_available if p not in params]
        )

        # If we have the connection_cache and a callback setup we process the diff (and maybe param changes)
        if self.connection_cache and (params_dt.added or params_dt.removed or backedup_complete_cb_ss is not None or self.cb_ss.qsize() > 0):
            try:
                if backedup_complete_cb_ss is not None:
                    # using the message backed up after latest diff process
                    cb_ss = backedup_complete_cb_ss
                    backedup_complete_cb_ss = None  #CAREFUL : untested...
                else:
                    cb_ss = self.cb_ss.get_nowait()
                # print("CC MSG !")  # if we didn't except on empty queue, so we got a message

            except Queue.Empty:

                return self.update_nodelta(params_dt)

            else:
                # if there was no change,
                # it means it s the first and we need to initialize with the full list
                if cb_ss.added is None and cb_ss.removed is None:
                    #print("CC COMPLETE !")
                    publishers = cb_ss.complete.get('publishers', [])
                    subscribers = cb_ss.complete.get('subscribers', [])
                    services = cb_ss.complete.get('services', [])
                    # NOT YET...
                    #params = cb_ss.complete.get('params', params)
                    topic_types = cb_ss.complete.get('topic_types', [])
                    service_types = cb_ss.complete.get('service_types', [])

                    # To convert to ROS master API format :
                    publishers = [[p, [n[0] for n in nset]] for p, nset in publishers.iteritems()]
                    subscribers = [[s, [n[0] for n in nset]] for s, nset in subscribers.iteritems()]
                    services = [[s, [n[0] for n in nset]] for s, nset in services.iteritems()]

                    #print("CC COMPLETEDDDD !")

                    # we go back to normal flow
                    #print("UPDATE FULLSTATE ON CACHE LIST")
                    return self.update_fullstate(publishers, subscribers, services, params, topic_types, service_types)

                else:  # we have a delta, we can use it directly and skip the rest

                    #print("CC DELTA !")
                    next_cb_ss = cb_ss
                    while self.cb_ss.qsize() > 0 and not (next_cb_ss.added is None and next_cb_ss is None):
                        # merging multiple diff messages as fast as possible, until the next complete status
                        next_cb_ss = self.cb_ss.get_nowait()
                        if not (next_cb_ss.added is None and next_cb_ss is None):
                            cb_ss = CacheTuple(
                                complete=None,  # this is ignored here
                                # CAREFUL one of added or remove can still be None here...
                                added=connection_cache_merge_marshalled(next_cb_ss.added or {}, cb_ss.added),
                                removed=connection_cache_merge_marshalled(next_cb_ss.removed or {}, cb_ss.removed)
                            )
                        else:  # we need to pass next_cb_ss to handle complete list again...
                            backedup_complete_cb_ss = next_cb_ss

                    added_publishers = cb_ss.added.get('publishers', [])
                    added_subscribers = cb_ss.added.get('subscribers', [])
                    added_services = cb_ss.added.get('services', [])
                    # NOT YET
                    #added_params = cb_ss.added.get('params', params_dt.added)
                    added_params = params_dt.added
                    added_topic_types = cb_ss.added.get('topic_types', [])
                    added_service_types = cb_ss.added.get('service_types', [])

                    removed_publishers = cb_ss.removed.get('publishers', [])
                    removed_subscribers = cb_ss.removed.get('subscribers', [])
                    removed_services = cb_ss.removed.get('services', [])
                    # NOT YET
                    #removed_params = cb_ss.removed.get('params', params_dt.removed)
                    removed_params = params_dt.removed
                    removed_topic_types = cb_ss.removed.get('topic_types', [])
                    removed_service_types = cb_ss.removed.get('service_types', [])

                    #print("UPDATE STATEDELTA ON CACHE DIFF")
                    return self.update_statedelta(
                        added_publishers, added_subscribers, added_services, added_params, added_topic_types, added_service_types,
                        removed_publishers, removed_subscribers, removed_services, removed_params, removed_topic_types, removed_service_types
                    )
        elif not self.connection_cache:  # make sure we are not using connection cache (otherwise state representations might be out of sync !!)
            #print("GETTING STATE FROM MASTER")
            publishers, subscribers, services, params, topic_types, service_types = self.retrieve_system_state()  # This will call the master

            #print("UPDATE FULLSTATE")
            # NOTE : we want to be certain here that we do not mix full state representation from master with representation from cache (out of sync !!!)
            return self.update_fullstate(publishers, subscribers, services, params, topic_types, service_types)
        else:
            # This happens when the update is triggered, we are supposed to use the cache, but we got no message from the connection cache proxy.
            # WE DO NOT WANT TO GET FULLSTATE FROM CACHE, since it is out of date (ex: removed publishers are not removed until next message...)
            # print("GETTING STATE FROM CACHE")
            # publishers, subscribers, services, params, topic_types, service_types = self.retrieve_system_state()  # This will call the cache
            #
            # print("UPDATE FULLSTATE")
            # return self.update_fullstate(publishers, subscribers, services, params, topic_types, service_types)
            # ==> We need to wait for next message...
            return self.update_nodelta(params_dt)

    def update_fullstate(self, publishers, subscribers, services, params, topic_types, service_types):
        # NORMAL full update
        self._debug_logger.debug("""SYSTEM STATE :
                    - publishers : {publishers}
                    - subscribers : {subscribers}
                    - services : {services}
                    - params : {params}
                    - topic_types : {topic_types}
                    - service_types : {service_types}
                """.format(**locals()))

        # TODO : unify with the reset behavior in case of cache...

        # Needs to be done first, since topic algorithm depends on it
        # print("PARAMS : {params}".format(**locals()))
        params_if_dt = self.params_if_pool.update(params=params)
        # print("PARAM IF DT : {params_if_dt}".format(**locals()))

        # print("SERVICES : {services}".format(**locals()))
        services_if_dt = self.services_if_pool.update(services, service_types)
        # print("SERVICE IF DT : {services_if_dt}".format(**locals()))

        # print("SUBSCRIBERS : {subscribers}".format(**locals()))
        subscribers_if_dt = self.subscribers_if_pool.update(subscribers, topic_types)
        # print("SUBSCRIBER IF DT : {subscribers_if_dt}".format(**locals()))

        # print("PUBLISHERS : {publishers}".format(**locals()))
        publishers_if_dt = self.publishers_if_pool.update(publishers, topic_types)
        # print("PUBLISHER IF DT : {publishers_if_dt}".format(**locals()))

        dt = DiffTuple(
            added=params_if_dt.added + services_if_dt.added + subscribers_if_dt.added + publishers_if_dt.added,
            removed=params_if_dt.removed + services_if_dt.removed + subscribers_if_dt.removed + publishers_if_dt.removed
        )

        self._debug_logger.debug("""
                    ROS INTERFACE ADDED : {dt.added}
                    ROS INTERFACE REMOVED : {dt.removed}
                """.format(**locals()))

        return dt

    def update_nodelta(self, params_dt):
        """
        Running updates of nothing (workaround until params are handled by cache)
        :param params_dt:
        :return:
        """
        # we re NOT done here, we might still need to update params
        params_if_dt = self.params_if_pool.update_delta(params_dt=params_dt)

        topic_types_dt = DiffTuple(
            added=[],
            removed=[]  # shouldnt matter
        )

        services_if_dt = DiffTuple([], [])
        # TMP : NOT dropping topics early (just be patient and wait for the cache callback to come...)
        # topics_if_dt = self.topics_pool.update_delta(topics_dt, topic_types_dt)
        subscribers_if_dt = DiffTuple([], [])
        publishers_if_dt = DiffTuple([], [])

        # and here we need to return to not do the normal full update
        dt = DiffTuple(
            added=params_if_dt.added + services_if_dt.added + subscribers_if_dt.added + publishers_if_dt.added,
            removed=params_if_dt.removed + services_if_dt.removed + subscribers_if_dt.removed + publishers_if_dt.removed
        )

        self._debug_logger.debug("""
                            ROS INTERFACE DIFF ADDED : {dt.added}
                            ROS INTERFACE DIFF REMOVED : {dt.removed}
                        """.format(**locals()))

        return dt

    def update_statedelta(
            self,
            added_publishers, added_subscribers, added_services, added_params, added_topic_types, added_service_types,
            removed_publishers, removed_subscribers, removed_services, removed_params, removed_topic_types, removed_service_types
    ):
        params_dt = DiffTuple(
            added=added_params,
            removed=removed_params
        )

        # Needs to be done first, since topic algorithm depends on it
        params_if_dt = self.params_if_pool.update_delta(params_dt=params_dt)

        # here we need to get only the nodes' names to match ROs master API format
        services_dt = DiffTuple(
            added=[[k, [n[0] for n in nset]] for k, nset in added_services.iteritems()],
            removed=[[k, [n[0] for n in nset]] for k, nset in removed_services.iteritems()]
        )

        service_types_dt = DiffTuple(
            added=added_service_types,
            removed=removed_service_types
        )

        services_if_dt = self.services_if_pool.update_delta(services_dt, service_types_dt)

        # here we need to get only the nodes' names to match ROs master API format
        # CAREFUL about unicity here, a Pub|Sub can have multiple endpoint within the same node...
        # We do not care about that information. If we have to care, then we should pass also the uri...
        publishers_dt = DiffTuple(
            added=[[k, [n[0] for n in set(nset)]] for k, nset in added_publishers.iteritems()],
            removed=[[k, [n[0] for n in set(nset)]] for k, nset in removed_publishers.iteritems()]
        )
        subscribers_dt = DiffTuple(
            added=[[k, [n[0] for n in set(nset)]] for k, nset in added_subscribers.iteritems()],
            removed=[[k, [n[0] for n in set(nset)]] for k, nset in removed_subscribers.iteritems()]
        )

        # NOW DONE IN update_delta
        # # CAREFUL, topic interface by itself also makes the topic detected on system
        # # Check if there are any pyros interface with it and ignore them
        # topics_to_drop = TopicBack.get_interface_only_topics()
        # # TODO : simplify : same as early_topics_to_drop ?
        #
        # # filtering the topic dict
        # for td, tnode in added_topics.iteritems():
        #     tnode = [n for n in tnode if n not in topics_to_drop.get(td, [])]
        #
        # added_topics_list = [[td, added_topics[td]] for td in added_topics if added_topics[td]]  # filtering out topics with no endpoints
        #
        # # we also need to simulate topic removal here (only names), to trigger a cleanup of interface if it s last one
        # removed_topics_list = [[td, removed_topics[td]] for td in removed_topics] + early_topics_to_drop

        topic_types_dt = DiffTuple(
            added=added_topic_types,
            removed=removed_topic_types
        )

        subscribers_if_dt = self.subscribers_if_pool.update_delta(subscribers_dt, topic_types_dt)
        publishers_if_dt = self.publishers_if_pool.update_delta(publishers_dt, topic_types_dt)

        if publishers_if_dt.added or publishers_if_dt.removed:
            self._debug_logger.debug(
                rospy.get_name() + " Pyros.ros : Publishers Delta {publishers_if_dt}".format(
                    **locals()))
        if subscribers_if_dt.added or subscribers_if_dt.removed:
            self._debug_logger.debug(
                rospy.get_name() + " Pyros.ros : Subscribers Delta {subscribers_if_dt}".format(
                    **locals()))
        if services_if_dt.added or services_if_dt.removed:
            self._debug_logger.debug(
                rospy.get_name() + " Pyros.ros : Services Delta {services_if_dt}".format(
                    **locals()))

        # TODO : put that in debug log and show based on python logger configuration
        # print("Pyros ROS interface UPDATE")
        # print("Params ADDED : {0}".format([p for p in params_dt.added]))
        # print("Params GONE : {0}".format([p for p in params_dt.removed]))
        # print("Topics ADDED : {0}".format([t[0] for t in topics_dt.added] + early_topics_dt.added))
        # print("Topics GONE : {0}".format([t[0] for t in topics_dt.removed] + early_topics_dt.removed))
        # print("Srvs ADDED: {0}".format([s[0] for s in services_dt.added]))
        # print("Srvs GONE: {0}".format([s[0] for s in services_dt.removed]))

        # update_on_diff wants only names
        # dt = super(RosInterface, self).update_on_diff(
        #         DiffTuple([s[0] for s in services_dt.added], [s[0] for s in services_dt.removed]),
        #         DiffTuple([t[0] for t in topics_dt.added] + early_topics_dt.added, [t[0] for t in topics_dt.removed] + early_topics_dt.removed),
        #         # Careful params_dt has a different content than service and topics, due to different ROS API
        #         # TODO : make this as uniform as possible
        #         DiffTuple([p for p in params_dt.added], [p for p in params_dt.removed])
        # )

        # and here we need to return to not do the normal full update
        dt = DiffTuple(
            added=params_if_dt.added + services_if_dt.added + subscribers_if_dt.added + publishers_if_dt.added,
            removed=params_if_dt.removed + services_if_dt.removed + subscribers_if_dt.removed + publishers_if_dt.removed
        )

        self._debug_logger.debug("""
                                        ROS INTERFACE DIFF ADDED : {dt.added}
                                        ROS INTERFACE DIFF REMOVED : {dt.removed}
                                    """.format(**locals()))

        return dt

    def _proxy_cb(self, system_state, added_system_state, lost_system_state):
        self.cb_ss.put(CacheTuple(
            complete=connection_cache_marshall(system_state),
            added=connection_cache_marshall(added_system_state) if added_system_state is not None else None,
            removed=connection_cache_marshall(lost_system_state) if lost_system_state is not None else None
        ))


import networkx as nx
import matplotlib.pyplot as plt
import random
import itertools

def get_signs_of_tris(triangle_list,G):
	#triangle_list=[[1,2,3][4,5,6][7,8,9]]
	#all_signs=[[1,2->'+',2,3->'-',1,3->'-'][][]]
	all_signs=[]
	for i in range(len(triangle_list)):
		temp=[]
		temp.append(G[ triangle_list[i][0]] [triangle_list[i][1]] ['sign'] )
		temp.append(G[ triangle_list[i][1]] [triangle_list[i][2]] ['sign'] )
		temp.append(G[ triangle_list[i][0]] [triangle_list[i][2]] ['sign'] )
		all_signs.append(temp)
	return all_signs

def count_unstable(all_signs):
	stable=0
	unstable=0
	for i in range(len(all_signs)):
		if all_signs[i].count('+') == 3 or all_signs[i].count('+') == 1:
			stable +=1
		elif all_signs[i].count('+') == 2 or all_signs[i].count('+') == 0:
			unstable +=1
	print ("total trianlges =" ,stable+unstable)
	print ("total unstable trianlges =", unstable)
	print ("total stable trianlges =", stable)
	return stable,unstable
#1. create a graph with 'n' nodes where nodes are game of thrones characters

def move_to_stable(G,triangle_list,all_signs):
	found_stable = False
	while(found_stable == False):
		index=random.randint(0,len(triangle_list)-1)
		if all_signs[index].count('+') == 2 or all_signs[index].count('+') == 0:
			found_stable = True
		else:
			continue
	#move unstable triangle to stable state
	r=random.randint(1,3)
	if all_signs[index].count('+') == 2:
	 	if r == 1:
	 		if (G[triangle_list[index][0]][triangle_list[index][1]]['sign']) == '+':
	 			G[triangle_list[index][0]][triangle_list[index][1]]['sign'] = '-'
	 		elif G[triangle_list[index][0]][triangle_list[index][1]]['sign'] == '-':
	 			G[triangle_list[index][0]][triangle_list[index][1]]['sign'] = '+'
	 	elif r==2:
	 		if G[triangle_list[index][1]][triangle_list[index][2]]['sign'] =='+':
	 			G[triangle_list[index][1]][triangle_list[index][2]]['sign'] ='-'
	 		elif G[triangle_list[index][1]][triangle_list[index][2]]['sign'] =='-':
	 			G[triangle_list[index][1]][triangle_list[index][2]]['sign'] ='+'
	 	else:
	 		if G[triangle_list[index][0]][triangle_list[index][2]]['sign'] =='+':
	 			G[triangle_list[index][0]][triangle_list[index][2]]['sign'] ='-'
	 		elif G[triangle_list[index][0]][triangle_list[index][2]]['sign'] =='-':
	 			G[triangle_list[index][0]][triangle_list[index][2]]['sign'] ='+'
	elif all_signs[index].count('+') == 0:
		if r==1:
			G[triangle_list[index][0]][triangle_list[index][1]]['sign'] ='+'
		elif r==2:
			G[triangle_list[index][1]][triangle_list[index][2]]['sign'] ='+'
		elif r==3:
			G[triangle_list[index][0]][triangle_list[index][2]]['sign'] ='+'
	return G

def see_coalitions(G):
	first_coalition = []
	second_coalition =[]
	node = G.nodes()
	r = random.choice(node)
	first_coalition.append(r)
	processed_nodes=[]
	to_be_processed =[r]

	# BFS STARTS HERE
	for each in to_be_processed:

		if each not in processed_nodes:
			neigh = G.neighbors(each)
			#print (neigh)

			for i in range(len(neigh)):
				if G[each][neigh[i]]['sign']=='+':
					if neigh[i] not in first_coalition:
						first_coalition.append(neigh[i])
					if neigh[i] not in to_be_processed:
						to_be_processed.append(neigh[i])
				elif G[each][neigh[i]]['sign']=='-':
					if neigh[i] not in second_coalition:
						second_coalition.append(neigh[i])
						processed_nodes.append(neigh[i])
			processed_nodes.append(each)
	return first_coalition,second_coalition

G = nx.Graph()
n=5
G.add_nodes_from([i for i in range(1 , n+1)])
mapping = {1:'Jon',2:'Cersai',3:'Tyrion',4:'Danny',5:'Jamie',6:'Sam'}
G = nx.relabel_nodes(G , mapping) #first one is list and second param is Graph

#2. make it a complete graph by adding all the possible edges.assign +ve or -ve edge weights

signs =[ '+','-']
for i in G.nodes():
	for j in G.nodes():
		if i!=j:
			G.add_edge(i,j, sign = random.choice(signs)) #random.choice randomly chooses one value

#3.display the network

edge_labels =nx.get_edge_attributes(G,'sign')
pos = nx.circular_layout(G)
nx.draw(G , pos ,node_size=3000,with_labels=True)
nx.draw_networkx_edge_labels(G,pos,edge_labels=edge_labels,font_size=20,font_color='red')
plt.show()

#4
	#1. get all triangles

nodes = G.nodes()
tri_list = [list(x) for x in itertools.combinations(nodes,3)]

	#2. store all their details

all_signs = get_signs_of_tris(tri_list, G) #list of lists [ [] ,[] ,[]]

	#3. count number of unstable triangles

stable,unstable = count_unstable(all_signs) 	#count the number of triangles using the list and then count the number of all unstable triangles and return them

#5 while number of unstable triangles is not 0
total=stable+unstable
while(unstable !=0 ):
	#1. choose a triangle in a graph that is unstable
	#2. make that triangle stable
	#3. count that number of unstable triangles
	G = move_to_stable(G,tri_list,all_signs)
	all_signs = get_signs_of_tris(tri_list,G)
	stable,unstable = count_unstable(all_signs)

edge_labels =nx.get_edge_attributes(G,'sign')
pos = nx.circular_layout(G)
nx.draw(G , pos ,node_size=3000,with_labels=True)
nx.draw_networkx_edge_labels(G,pos,edge_labels=edge_labels,font_size=20,font_color='red')
plt.show()
#6 here no unstable triangle, partition it into two clusters
	#1. choose a random node. add it to first coalition
	#2. Also put all the 'friends' of this node in first coalition
	#3. put all the enemies of this node in the second coalition

first,second = see_coalitions(G)
print (first)
print (second)
edge_labels =nx.get_edge_attributes(G,'sign')
pos = nx.circular_layout(G)
nx.draw(G , pos ,node_size=3000,with_labels=True)
nx.draw_networkx_edge_labels(G,pos,edge_labels=edge_labels,font_size=20,font_color='red')
plt.show()


#Importando Librerias
import re
import tkinter as tk
from tkinter import messagebox
from ply import lex, yacc
from subprocess import call

#Definir los tokens
tokens = (

    'WHILE',
    'SYSTEM',
    'OUT',
    'INT',
    'ID',
    'NUM',
    'STRING',
    'PLUS',
    'SEMICOLON',
    'LPAREN',
    'RPAREN',
    'LBRACE',
    'RBRACE',
    'DOT',
    'EQUALS',
    'LEQ',
    "MAYOR",
    "MENOR",
    "COM",
    'PRINTLN',

)
t_PLUS = r'\+'
t_SEMICOLON = r';'
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_LBRACE = r'{'
t_RBRACE = r'}'
t_DOT = r'\.'
t_EQUALS = r'='
t_LEQ = r'<='
t_MAYOR = r'>'
t_MENOR = r'<'
t_COM = r'"'

def t_STRING(t):
    r'\".*?\"'
    t.value = t.value[1:-1]
    return t

def t_ID(t):
    r'[a-zA-Z][a-zA-Z0-9]*'
    if t.value == 'while':
        t.type = 'WHILE'
    elif t.value == 'int':
        t.type = 'INT'
    elif t.value == 'println':
        t.type = 'PRINTLN'
    elif t.value == 'System':
        t.type = 'SYSTEM'
    elif t.value == 'out':
        t.type = 'OUT'
    return t
# Regla para identificar números
def t_NUM(t):
    r'\d+'
    t.value = int(t.value)
    return t

# Ignorar espacios en blanco y saltos de línea
t_ignore = ' \t\n'

def t_error(t):
    error_message(f"Token desconocido '{t.value[0]}'", t.lineno)
    t.lexer.skip(1)

# Construcción del lexer
lexer = lex.lex()

# Variable global para verificar si la sintaxis es correcta
is_syntax_correct = True
error_info = None  # Almacena información sobre el error si hay alguno

# Definición de la gramática para el análisis sintáctico
def p_while_loop(p):
    '''while_loop : INT ID EQUALS NUM SEMICOLON WHILE LPAREN ID LEQ NUM RPAREN LBRACE SYSTEM DOT OUT DOT PRINTLN LPAREN ID RPAREN SEMICOLON ID PLUS PLUS SEMICOLON RBRACE'''
    pass

# Manejo de errores de sintaxis
def p_error(p):
    global is_syntax_correct, error_info
    is_syntax_correct = False
    if p:
        error_info = f"Error de sintaxis en '{p.value}' en la línea {p.lineno}"
    else:
        error_info = "Error de sintaxis: final inesperado del código"

# Construcción del parser
parser = yacc.yacc()

# Función para el análisis léxico
def lex_analyzer(code):
    lexer.input(code)
    tokens = []
    while True:
        token = lexer.token()
        if not token:
            break
        tokens.append((token.lineno, token.type, token.value))
    return tokens

# Función para el análisis sintáctico
def parse_code(code):
    parser.parse(code, lexer=lexer)

def error_message(message, line_number):
    messagebox.showerror("Error de sintaxis", f"{message}\n En la línea {line_number}")

# Función para procesar el código ingresado
def process_code():
    global is_syntax_correct, error_info
    code = code_text.get("1.0", "end-1c")
    tokens = lex_analyzer(code)
    result_text.delete("1.0", "end")
    is_syntax_correct = True
    error_info = None
    try:
        parser.parse(code, lexer=lexer)
    except Exception as e:
        is_syntax_correct = False
        error_info = f"Error durante el análisis sintáctico: {e}"

    for token in tokens:
        line_number, token_type, token_value = token
        result_text.insert("end", f"Línea ->: {token_type} -> {token_value}\n")

    if is_syntax_correct:
        messagebox.showinfo("Análisis Sintáctico", "Todo está correcto.")
    else:
        messagebox.showerror("Error de Sintaxis", error_info)

def regresar():
    window.destroy()
    call(["python", "main.py"])

# Creación de la ventana de la interfaz gráfica
window = tk.Tk()
window.title("Analizador Sintactico")
window.geometry("490x590")
window.configure(bg='#FFF2D8')

#Crea una etiqueta para el titulo de la aplicacion
text_label = tk.Label(window, text="----- ANALIZADOR SINTÁCTICO -----", height=1, width=50, font=("Arial", 20, 'bold'), fg="#FFF3DA", bg="#141E46")
text_label.pack()

# Etiqueta y campo de texto para ingresar el código
code_label = tk.Label(window, text="Ingrese el código:", font=("ARIAL", 14), bg='#FFF2D8')
code_label.pack(pady=10)

code_text = tk.Text(window, height=10, width=50)
code_text.pack()

# Botón para procesar el código
#process_button = tk.Button(window, text="Procesar", command=process_code, font=("ARIAL", 14),bg='#64CCC5')
#process_button.pack(pady=10)

# Crear un frame para los botones
button_frame = tk.Frame(window, bg='#FFF2D8')
button_frame.pack(pady=10)

# Botón para procesar el código
process_button = tk.Button(button_frame, text="Analizar", command=process_code, font=("ARIAL", 14), bg="#A8DF8E")
process_button.grid(row=0, column=0, padx=5)

# Botón para limpiar el código ingresado
clear_button = tk.Button(button_frame, text="Limpiar", command=lambda: (code_text.delete("1.0", "end-1c"), result_text.delete("1.0", "end")), font=("ARIAL", 14), bg="#FFC436")
clear_button.grid(row=0, column=1, padx=5)

# Botón para salir del programa
exit_button = tk.Button(button_frame, text="Salir", command=window.destroy, font=("ARIAL", 14), bg="#FF6969")
exit_button.grid(row=0, column=2, padx=5)

# Etiqueta y campo de texto para mostrar los tokens
result_label = tk.Label(window, text="Tokens:", font=("ARIAL", 14), bg='#FFF2D8')
result_label.pack(pady=5)

result_text = tk.Text(window, height=10, width=50)
result_text.pack()

regresar_button = tk.Button(window, text="Regresar al menú", command=regresar, font=("Arial", 16, 'bold'), bg="#61A3BA")
regresar_button.pack(pady=20)

# Ejecución de la interfaz gráfica
window.mainloop()

SUB4/Act. 4.1 Proyecto Final Analizadores/Proyecto Final Compiladores/sintactico.py


# Photo.py
import io
import os
from enum import Enum, unique
import hashlib
from typing import List
from datetime import datetime
import base64

from mongoengine import (
    Document,
    IntField,
    FloatField,
    StringField,
    ListField,
    ReferenceField,
    BooleanField,
    ImageField,
    DateTimeField,
    DictField,
    EmailField,
    EnumField,
    URLField,
)
import numpy as np
from tqdm import tqdm
from mongoengine import signals
from PIL import Image, ImageDraw, ImageFont
from pkg_resources import require
from PIL.ExifTags import GPSTAGS, TAGS
from PIL.TiffImagePlugin import IFDRational
from retinaface import RetinaFace
from deepface.basemodels import ArcFace
from deepface.commons import distance as dst
from hachoir.parser import createParser
from hachoir.metadata import extractMetadata

from . import logger, db
from .config import Config
from .Face import Face
from .utils.db import handler
from .Address import Address, get_decimal_coordinates
from .Face import Face
from .Person import Person

def read_metadata(pth: str, write_md: bool = False) -> dict:
    im = Image.open(pth)
    exif_data = im._getexif()
    _, fn = os.path.split(pth)
    bn, _ = os.path.splitext(fn)

    if write_md:
        os.makedirs("md", exist_ok=True)
        f_md = open("md/metadata_%s.txt" % fn, "src_w")
        f_md.write("%s\n" % pth)
        f_md.write("%s\n" % exif_data)
        im.close()

    if exif_data is None:
        exif_data = {}

    lkeys = list(exif_data.keys())
    for key in lkeys:
        name = TAGS.get(key, key)
        exif_data[name] = exif_data.pop(key)

    if "GPSInfo" in exif_data:
        lkeys = list(exif_data["GPSInfo"].keys())
    elif "34853" in exif_data:
        lkeys = list(exif_data["34853"].keys())
        exif_data["GPSInfo"] = {}
    else:
        lkeys = []
        exif_data["GPSInfo"] = {}

    for key in lkeys:
        name = GPSTAGS.get(key, key)
        exif_data["GPSInfo"][name] = exif_data["GPSInfo"].pop(key)

    orig_exif = stringify_keys(exif_data)

    if not "Orientation" in exif_data.keys():
        exif_data["Orientation"] = 1

    if "DateTimeOriginal" in exif_data.keys():
        date_time_str = exif_data["DateTimeOriginal"]
        # date_time_str='2020:04:02 12:58:12'
        exif_data["InferredDateTime"] = False
        dt = datetime.strptime(date_time_str, "%Y:%m:%d %H:%M:%S")
    else:
        # bn="Photo 20-03-13 17-53-52 0558"
        exif_data["InferredDateTime"] = True
        dt = datetime.strptime(bn, "Photo %y-%m-%d %H-%M-%S %f")

    exif_data["DateTimeOriginal"] = dt

    lat, lon = get_decimal_coordinates(exif_data["GPSInfo"])
    exif_data["GPSInfo"]["Latitude"] = lat
    exif_data["GPSInfo"]["Longitude"] = lon

    if write_md:
        f_md.write("%s\n" % exif_data)
        f_md.close()

    return orig_exif, exif_data

def crop_face(img: Image, height: int, force_square=True) -> Image:
    src_w, src_h = img.size
    sze_h = height
    sze_w = (sze_h * src_w) // src_h

    if force_square:
        tgt_h = height
        tgt_w = height
    else:
        tgt_h = height
        tgt_w = tgt_h * src_w / src_h

    rect_face = img.resize((sze_w, sze_h))

    # Resizing the face
    # and adding black pixels if the extracted face is not square
    img = Image.new(mode="RGB", size=(tgt_w, tgt_h), color=(0, 0, 0))
    if sze_w > sze_h:
        img.paste(rect_face, (0, tgt_w // 2 - sze_h // 2))
    else:
        img.paste(rect_face, (tgt_h // 2 - sze_w // 2, 0))

    return img

def stringify_keys(md: dict) -> dict:
    ret = {}
    for k in md.keys():
        sk = str(k)
        if isinstance(md[k], dict):
            sv = stringify_keys(md[k])
        else:
            sv = md[k]
            if isinstance(sv, IFDRational):
                sv = float(sv)
            elif hasattr(sv, "__iter__") and not isinstance(sv, str):
                svl = []
                for svk in sv:
                    if isinstance(svk, IFDRational):
                        if svk.denominator == 0:
                            svk = np.nan
                        else:
                            svk = float(svk)
                    svl.append(svk)
                sv = svl

        ret[sk] = sv

    return ret

face_detector = RetinaFace.build_model()
face_recog = ArcFace.loadModel()
face_recog.load_weights(
    os.path.expandvars("$HOME/.deepface/weights/arcface_weights.h5")
)

def detect_face(photo: Image) -> List[dict]:
    orig_arr = np.array(photo)

    detec = RetinaFace.detect_faces(orig_arr, model=face_detector)
    if not hasattr(detec, "keys"):
        detec = {}

    faces = []
    for kd in detec.keys():
        # Coordinates of the face the picture
        left, upper, right, lower = detec[kd]["facial_area"]

        # Crop the picture to extract the face
        rect_face = photo.crop((left, upper, right, lower))

        # Resizing the face (112,112)
        img = crop_face(rect_face, height=112, force_square=True)

        # Hash of the face
        buf = io.BytesIO()
        img.save(buf, format="JPEG")
        buf = buf.getvalue()
        h = hashlib.sha224(buf).hexdigest()

        # Embedding of the face
        img = np.array(img, dtype=np.float32)
        img_pixels = np.expand_dims(img, axis=0)
        img_pixels /= 255  # normalize input in [0, 1]
        embedding = face_recog.predict(img_pixels)[0]

        face_db = dict(
            hash=h,
            embedding=embedding,
            left=left,
            right=right,
            lower=lower,
            upper=upper,
            manually_tagged=False,
            detection_score=detec[kd]["score"],
            # landmarks=detec[kd]["landmarks"],
        )
        faces.append(face_db)

    logger.debug("%i faces detected" % len(faces))

    return faces

@handler(signals.pre_delete)
def photo_suppressed(sender, document):
    logger.debug("Suppress photo %s (%s)" % (document.id, document.original_path))
    for face in document.faces:
        logger.debug("   Suppress face %s" % face.id)
        face.delete()

    if not document.place_taken is None:
        document.place_taken.photos.remove(document)

    if not document.album is None:
        document.album.photos.remove(document)

@photo_suppressed.apply
class Photo(db.Document):
    photo = ImageField()
    original_path = StringField(required=True)
    hash = StringField(unique=True, required=True)
    place_taken = ReferenceField("Address")
    miniature = ImageField(thumbnail_size=(200, 200))
    date_taken = DateTimeField(required=True)
    faces = ListField(ReferenceField(Face))
    album = ReferenceField("Album")
    inferred_date = BooleanField()
    original_exif = DictField()

    meta = {"ordering": ["date_taken"]}

    @classmethod
    def showPhoto(cls, show_faces=False, **kwargs):
        photo = cls.objects(**kwargs).first()
        image = photo.photo.read()
        img = Image.open(io.BytesIO(image))
        font = ImageFont.truetype("Arial Unicode.ttf", size=15)

        if show_faces:
            # Drawing a red rectangle on the photo to locate the person
            img_with_red_box_draw = ImageDraw.Draw(img)
            for face in photo.faces:
                if face.manually_tagged:
                    color = "green"
                else:
                    color = "red"

                img_with_red_box_draw.rectangle(
                    [(face.left, face.upper), (face.right, face.lower)],
                    outline=color,
                    width=3,
                )
                if not face.person is None:
                    rec = face.person.complete_name
                    nom = rec["Nom complet"]
                else:
                    nom = "Unknown"
                img_with_red_box_draw.text(
                    (face.left, face.lower), nom, font=font, fill=color
                )

        img.show()

    def show(self, show_faces=True):
        image = self.photo.read()
        img = Image.open(io.BytesIO(image))

        if show_faces:
            img_with_red_box_draw = ImageDraw.Draw(img)

            for face in self.faces:
                if face.manually_tagged:
                    color = "green"
                else:
                    color = "red"
                img_with_red_box_draw.rectangle(
                    [(face.left, face.upper), (face.right, face.lower)],
                    outline=color,
                    width=3,
                )

        img.show()

    @classmethod
    def importFile(cls, pth: str, recognize: bool = False) -> "Photo":
        face_dst_dir = "faces"
        os.makedirs(face_dst_dir, exist_ok=True)

        logger.info(72 * "=")
        logger.info(pth)

        # ======================
        # Lecture des metadata
        # ======================
        orig_exif, metadata = read_metadata(pth)
        gps_info = metadata.get("GPSInfo", {})
        lat = gps_info.get("Latitude", None)
        lon = gps_info.get("Longitude", None)
        alt = gps_info.get("GPSAltitude", 0)
        if not lon is None and not lat is None:
            place_taken = Address.fromLatLonAlt(lat, lon, alt)
        else:
            logger.warning("No location data")
            place_taken = None
        inferred_dt = metadata["InferredDateTime"]
        date_taken = metadata.get("DateTimeOriginal", None)

        # ======================
        # Photo creation
        # ======================
        img = Image.open(pth)
        buf = io.BytesIO()
        img.save(buf, format="JPEG")
        src_h = hashlib.sha224(buf.getvalue()).hexdigest()

        if cls.objects(hash=src_h).count() > 0:
            dup = cls.objects(hash=src_h).first()
            logger.warning("Duplicate photo (skipping):")
            logger.warning("%s" % pth)
            logger.warning("%s" % dup.original_path)
            return

        if date_taken is None:
            photo = cls(
                hash=src_h,
                original_exif=orig_exif,
                inferred_date=inferred_dt,
                original_path=pth,
            )
            logger.warning("No date and time data")
        else:
            photo = cls(
                hash=src_h,
                original_exif=orig_exif,
                inferred_date=inferred_dt,
                original_path=pth,
                date_taken=date_taken,
            )

        if not place_taken is None:
            photo.save()
            photo.place_taken = place_taken
            place_taken.photos.append(photo)
            place_taken.save()

        # ======================
        # Orientation correction
        # ======================
        # angle – In degrees counter clockwise
        if metadata["Orientation"] == 8:
            angle = 90
        elif metadata["Orientation"] == 3:
            angle = 180
        elif metadata["Orientation"] == 6:
            angle = -90
        else:
            angle = 0

        if angle != 0:
            logger.debug("Rotated a=%i deg, %s" % (angle, photo.id))
            img = img.rotate(angle=angle)

        # ======================
        # Miniature creation
        # ======================
        src_w, src_h = img.size
        if src_w < src_h:
            mini = img.resize(
                size=(200, 200),
                resample=Image.HAMMING,
                box=(0, (src_h - src_w) // 2, src_w, src_w),
            )
        else:
            mini = img.resize(
                size=(200, 200),
                resample=Image.HAMMING,
                box=((src_w - src_h) // 2, 0, src_h, src_h),
            )

        # ======================
        # Enregistrement photo et miniature
        # ======================
        with io.BytesIO() as buf:
            mini.save(buf, format="JPEG")
            photo.miniature.replace(buf, filename=pth)

        with io.BytesIO() as buf:
            img.save(buf, format="JPEG")
            photo.photo.replace(buf, filename=pth)

        photo.save()

        # ======================
        # Faces detection
        # ======================
        lfaces = detect_face(img)
        for face_info in lfaces:
            q = Face.objects(hash=face_info["hash"])
            if q.count() > 0:
                face = q.first()
                logger.debug("Found Face already existing")
            else:
                face = Face(photo=photo, **face_info)
                face.save()
            if recognize:
                matching, corr = face.recognize_face()
                if not matching is None:
                    face.person = matching
                    face.recognition_score = corr
                    face.save()
            photo.faces.append(face)
        photo.save()

        return photo

    def getB64Miniature(self) -> bytes:
        image = self.miniature.read()
        b64 = base64.b64encode(image)
        return b64

    @property
    def date_elements(self):
        photo_year = self.date_taken.year
        photo_month = self.date_taken.month
        photo_month_name = datetime.strftime(self.date_taken, "%B")
        photo_day = self.date_taken.day
        photo_id = self.id
        return photo_year, photo_month, photo_month_name, photo_day, photo_id


from http import HTTPStatus

class CouldNotLoginGnocchi(Exception):
    def __init__(self, id=None, secret=None):
        self.status_code = HTTPStatus.UNAUTHORIZED
        if id and secret:
            self.message = f'Could not login on OpenStack"'
        else:
            self.message = 'Could not get authentication token from OpenStack'
        super().__init__(self.message)

    def __str__(self):
        return self.message


from PyQt5.QtCore import Qt, QTimer, QTime
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (QWidget, QApplication,
                             QLabel, QPushButton,
                             QLineEdit, QHBoxLayout, QHBoxLayout)
from instr import *
from final_win import *

class Experiment():
    def __init__(self, age, test1, test2, test3):
        self.age = age
        self.t1 = test1
        self.t2 = test2
        self.t3 = test3

class TestWin(QWidget):
    '''Класс второго окна с тестами и с теми же методами, что имеет начальное ознакомительное окно'''
    def __init__(self):
        super().__init__()
        self.initUI()
        self.connects()
        self.set_appear()
        self.show()

    def initUI(self):
        # создаем виджеты кнопок
        self.button_next = QPushButton(txt_sendresults)
        self.button_test1 = QPushButton(txt_starttest1)
        self.button_test2 = QPushButton(txt_starttest2)
        self.button_test3 = QPushButton(txt_starttest3)

        # создаем виджеты текста
        self.name_txt = QLabel(txt_name)
        self.age_txt = QLabel(txt_age)
        self.test1_txt = QLabel(txt_test1)
        self.test2_txt = QLabel(txt_test2)
        self.test3_txt = QLabel(txt_test3)
        self.timer_txt = QLabel(txt_timer)

        # создаем виджеты полей ввода
        self.l_name = QLineEdit(txt_hintname)
        self.l_age = QLineEdit(txt_hintage)
        self.l_test1 = QLineEdit(txt_hinttest1)
        self.l_test2 = QLineEdit(txt_hinttest2)
        self.l_test3 = QLineEdit(txt_hinttest3)

        # создаем направляющие линии
        self.l_line = QVBoxLayout() # правая вертикальная
        self.r_line = QVBoxLayout() # левая вертикальная
        self.h_line = QHBoxLayout() # основная центральная горизонтальная

        self.r_line.addWidget(self.timer_txt, alignment = Qt.AlignCenter) # таймер будет справа, а все остальное слева
        self.l_line.addWidget(self.name_txt, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.l_name, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.age_txt, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.l_age, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.test1_txt, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.button_test1, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.l_test1, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.test2_txt, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.button_test2, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.test3_txt, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.button_test3, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.l_test2, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.l_test3, alignment = Qt.AlignLeft)
        self.l_line.addWidget(self.button_next, alignment = Qt.AlignLeft)

        # обьединяем линии
        self.h_line.addLayout(self.l_line)
        self.h_line.addLayout(self.r_line)

        self.setLayout(self.h_line)

    # функция для нажатия кнопки для перехода в следующее окно
    def next_click(self):
        self.hide()
        self.exp = Experiment(int(self.l_age.text()), self.l_test1.text(), self.l_test2.text(), self.l_test3.text())
        self.fw = FinalWin(self.exp)

    # "коннектим" кнопку и метод next_click
    def connects(self):
        self.button_next.clicked.connect(self.next_click)
        self.button_test1.clicked.connect(self.timer_test)
        self.button_test2.clicked.connect(self.timer_sits)
        self.button_test3.clicked.connect(self.timer_final)

    # настраиваем окно(название, размер, координаты появления)
    def set_appear(self):
        self.setWindowTitle(txt_title)
        self.resize(win_width, win_height)
        self.move(win_x, win_y)

    def timer_test(self):
        global time
        time = QTime(0, 0, 15)
        self.timer = QTimer()
        self.timer.timeout.connect(self.timer1Event)
        self.timer.start(1000)
    def timer1Event(self):
        global time
        time = time.addSecs(-1)
        self.timer_txt.setText(time.toString("hh:mm:ss"))
        self.timer_txt.setStyleSheet("color: rgb(0,0,0)")
        self.timer_txt.setFont(QFont("Times", 36, QFont.Bold))
        if time.toString("hh:mm:ss") == "00:00:00":
            self.timer.stop()

    def timer_sits(self):
        global time
        time = QTime(0, 0, 30)
        self.timer = QTimer()
        self.timer.timeout.connect(self.timer2Event)
        self.timer.start(1500)
    def timer2Event(self):
        global time
        time = time.addSecs(-1)
        self.timer_txt.setText(time.toString("hh:mm:ss")[6:])
        self.timer_txt.setStyleSheet("color: rgb(0, 0, 0)")
        self.timer_txt.setFont(QFont("Times", 36, QFont.Bold))
        if time.toString("hh:mm:ss")[6:] == "00":
            self.timer.stop()

    def timer_final(self):
        global time
        time = QTime(0, 1, 0)
        self.timer = QTimer()
        self.timer.timeout.connect(self.timer3Event)
        self.timer.start(1000)
    def timer3Event(self):
        global time
        time = time.addSecs(-1)
        self.timer_txt.setText(time.toString("hh:mm:ss"))
        if int(time.toString("hh:mm:ss")[6:]) >= 45:
            self.timer_txt.setStyleSheet("color: rgb(0, 255, 0)")
        elif int(time.toString("hh:mm:ss")[6:]) <= 15:
            self.timer_txt.setStyleSheet("color: rgb(0, 255, 0)")
        else:
            self.timer_txt.setStyleSheet("color: rgb(0, 0, 0)")
        self.timer_txt.setFont(QFont("Times", 36, QFont.Bold))
        if time.toString("hh:mm:ss") == "00:00:00":
            self.timer.stop()


import copy
import logging

import numpy as np
import torch
from d2go.data.dataset_mappers import (
    D2GoDatasetMapper,
    D2GO_DATA_MAPPER_REGISTRY,
)
from d2go.data.dataset_mappers.d2go_dataset_mapper import (
    PREFETCHED_SEM_SEG_FILE_NAME,
    read_image_with_prefetch,
)
from detectron2.data import detection_utils as utils, transforms as T

# from d2go.data.fb import detection_utils as d2go_detection_utils

from detectron2.data.transforms.augmentation import (
    AugInput,
    AugmentationList,
)

logger = logging.getLogger(__name__)

@D2GO_DATA_MAPPER_REGISTRY.register()
class MetalearnDatasetMapper(D2GoDatasetMapper):
    """
    Mostly the same with D2GoDatasetMapper, with the change of need_annotation.

    Map a dataset_dict which is a dict from
    "query_set" : 2-d list, c * sq
    "support_set": 2-d list, c * s
    To:
    "query_set" : 1d list, c * sq
    "support_set": 1d list, c * s
    Each item is mapped from annotations to instances, and have the following form:
    {'file_name': 'dataset_path/000000442761.jpg', 'height': 333, 'width': 500, \
        'image_id': 442761, 'image': tensor(), \
        'instances': Instances(num_instances=1, image_height=800, image_width=1201, \
        fields=[gt_boxes: Boxes(tensor([[336.4241, 332.5886, 616.8336, 625.6096]])), gt_classes: tensor([15])])}
    """

    def __init__(
        self, cfg, is_train=True, need_annotation=True, image_loader=None, tfm_gens=None
    ):
        super().__init__(cfg, is_train, image_loader, tfm_gens)
        self.need_annotation = need_annotation

    def _original_call_per_item(self, dataset_dict):
        """
        Modified from d2go's _original_call in D2GoDatasetMapper. The only change is:
        self.need_annotation can happen regarding is_train or not. This is added for
        generating support set while class registration in episodic learning stage
        """
        # new add-on
        # Decouple the two dataset_dicts when in copy-paste mode
        if isinstance(dataset_dict, list):
            assert len(dataset_dict) == 2
            dataset_dict, dataset_dict2 = dataset_dict
        # it will be modified by code below
        dataset_dict = copy.deepcopy(dataset_dict)

        image = self._read_image(dataset_dict, format=self.img_format)

        if not self.backfill_size:
            utils.check_image_size(dataset_dict, image)
        image, dataset_dict = self._custom_transform(image, dataset_dict)

        # Annotation of the dataset dict
        if "annotations" in dataset_dict:
            anno = dataset_dict["annotations"]

        # if self.copy_paste_aug:
        #     # Prepare the second sample for pasting
        # image2 = self._read_image(dataset_dict2, format=self.img_format)
        # if not self.backfill_size:
        #     utils.check_image_size(dataset_dict2, image2)

        # image2, dataset_dict2 = self._custom_transform(
        #     image2, dataset_dict2)

        # # Annotation of the dataset dict
        # anno2 = dataset_dict2["annotations"]

        # # Construct AugDualInput object for CopyPaste augmentation
        # inputs = AugDualInput(
        #     image,
        #     anno=anno,
        #     image_b=image2,
        #     anno_b=anno2,
        # )
        # else:

        inputs = AugInput(image)
        if "annotations" not in dataset_dict:
            transforms = AugmentationList(
                ([self.crop_gen] if self.crop_gen else []) + self.tfm_gens
            )(inputs)
            # # Cache identical transforms in dataset_dict for subclass mappers
            dataset_dict["transforms"] = transforms
            image = inputs.image
        else:
            # pass additional arguments, will only be used when the Augmentation
            #   takes `annotations` as input
            inputs.annotations = dataset_dict["annotations"]
            # Crop around an instance if there are instances in the image.
            if self.crop_gen:
                crop_tfm = utils.gen_crop_transform_with_instance(
                    self.crop_gen.get_crop_size(image.shape[:2]),
                    image.shape[:2],
                    np.random.choice(dataset_dict["annotations"]),
                )
                inputs.image = crop_tfm.apply_image(image)

            transforms = AugmentationList(self.tfm_gens)(inputs)
            image = inputs.image
            if self.crop_gen:
                transforms = crop_tfm + transforms

        image_shape = image.shape[:2]
        if image.ndim == 2:
            image = np.expand_dims(image, 2)
        dataset_dict["image"] = torch.as_tensor(
            image.transpose(2, 0, 1).astype("float32")
        )
        # Can use uint8 if it turns out to be slow some day
        if self.load_proposals:
            utils.transform_proposals(
                dataset_dict,
                image_shape,
                transforms,
                proposal_topk=self.proposal_topk,
                min_box_size=self.proposal_min_box_size,
            )
        if not self.need_annotation:
            dataset_dict.pop("annotations", None)
            dataset_dict.pop("sem_seg_file_name", None)
            return dataset_dict
        if "annotations" in dataset_dict:
            for anno in dataset_dict["annotations"]:
                if not self.mask_on:
                    anno.pop("segmentation", None)
                if not self.keypoint_on:
                    anno.pop("keypoints", None)
            # Update the annotations of the background sample (mainly the occlusion info.)
            # Use the d2go version of transform_instances_annotations() to update occlusion
            # label of keypoints and the masks.
            annos = [
                utils.transform_instance_annotations(
                    obj,
                    transforms,
                    image_shape,
                    keypoint_hflip_indices=self.keypoint_hflip_indices,
                )
                for obj in dataset_dict.pop("annotations")
                if obj.get("iscrowd", 0) == 0
            ]

            # Transforms the second template sample to paste onto the background
            # if not isinstance(transforms[0], T.NoOpTransform):
            #     # Concatenate the original annotations and the pasted annotations
            #     annos2 = [
            #         utils.transform_instance_annotations(
            #             obj,
            #             transforms[1:],
            #             image_shape,
            #             keypoint_hflip_indices=self.keypoint_hflip_indices,
            #         )
            #         for obj in transforms[0].template_anno
            #         if obj.get("iscrowd", 0) == 0
            #     ]
            #     # Combine the annotations
            #     annos = annos + annos2

            instances = utils.annotations_to_instances(
                annos, image_shape, mask_format=self.mask_format
            )
            # Create a tight bounding box from masks, useful when image is cropped
            if self.crop_gen and instances.has("gt_masks"):
                instances.gt_boxes = instances.gt_masks.get_bounding_boxes()
            dataset_dict["instances"] = utils.filter_empty_instances(instances)

        if "sem_seg_file_name" in dataset_dict:
            sem_seg_gt = read_image_with_prefetch(
                dataset_dict.pop("sem_seg_file_name"),
                "L",
                prefetched=dataset_dict.get(
                    PREFETCHED_SEM_SEG_FILE_NAME, None),
            )
            if len(sem_seg_gt.shape) > 2:
                sem_seg_gt = sem_seg_gt.squeeze(2)
            sem_seg_gt = transforms.apply_segmentation(sem_seg_gt)
            sem_seg_gt = torch.as_tensor(sem_seg_gt.astype("long"))
            dataset_dict["sem_seg"] = sem_seg_gt

        # extend standard D2 semantic segmentation to support multiple segmentation
        # files, each file can represent a class
        if "multi_sem_seg_file_names" in dataset_dict:
            raise NotImplementedError()

        if "_post_process_" in dataset_dict:
            proc_func = dataset_dict.pop("_post_process_")
            dataset_dict = proc_func(dataset_dict)
        return dataset_dict

    def _original_call(self, dataset_dict):
        """
        This is added to process an episodic data item where a support set is a list of lists.
        It calles D2GoDatasetMapper._original_call to process an image item,
        and self._original_call_per_item(data) to reserve annotation in test stage for support set
        """
        mapped_dataset_dict = copy.deepcopy(dataset_dict)
        assert isinstance(mapped_dataset_dict, dict)
        if "support_set" not in dataset_dict:
            data = D2GoDatasetMapper._original_call(self, dataset_dict)
            # data = self._original_call_per_item(dataset_dict)
            return data

        if "support_set_target" in dataset_dict:
            mapped_dataset_dict["support_set_target"] = torch.tensor(
                mapped_dataset_dict["support_set_target"]
            )

        # Update  support set and query_set
        # when it is support set, it always needs annotation
        # needs to ensure we always sample enough data even after the jittering.
        if "support_set" in dataset_dict.keys():
            # mapped_dataset_dict["support_set"] = []
            mapped_data_list = []
            numer_of_empty_instances = 0
            for data in dataset_dict["support_set"]:
                mapped_data = self._original_call_per_item(data)
                # print(f"mapped data: {mapped_data}, len instances: {len(mapped_data['instances'])}")
                # non_empty_boxes = mapped_data["instances"].gt_boxes.nonempty(threshold=1e-5)
                if len(mapped_data["instances"]) != 0:
                    mapped_data_list.append(mapped_data)
                else:
                    numer_of_empty_instances += 1
            # print(f"entering support set: {numer_of_empty_instances}")
            if numer_of_empty_instances > 0:
                repeat_data_list = copy.deepcopy(np.random.choice(
                    mapped_data_list, numer_of_empty_instances, replace=True))
                mapped_data_list.extend(repeat_data_list)
            assert len(mapped_data_list) == len(dataset_dict["support_set"])
            mapped_dataset_dict["support_set"] = mapped_data_list
            # print(f"mapped_data_list: {len(mapped_data_list)}")

        if "query_set" in dataset_dict:
            mapped_dataset_dict["query_set"] = []
            for data in dataset_dict["query_set"]:
                mapped_data = D2GoDatasetMapper._original_call(self, data)
                mapped_dataset_dict["query_set"].append(mapped_data)
        # print(f"change with data aug: {mapped_dataset_dict}")
        # map the support_set_target
        return mapped_dataset_dict


# coding=utf-8
import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import argparse
import numpy as np
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, UpSampling2D, BatchNormalization, Reshape, Permute, Activation, Input
from keras.utils.np_utils import to_categorical
from keras.preprocessing.image import img_to_array
from keras.callbacks import ModelCheckpoint
from sklearn.preprocessing import LabelEncoder
from keras.models import Model
from keras.layers.merge import concatenate
from PIL import Image
import matplotlib.pyplot as plt
import cv2
import random
import os
from tqdm import tqdm

from keras.models import *
from keras.layers import *
from keras.optimizers import *

from keras import backend as K
# K.set_image_dim_ordering('th')
K.set_image_dim_ordering('tf')

os.environ["CUDA_VISIBLE_DEVICES"] = "2"
seed = 7
np.random.seed(seed)

img_w = 256
img_h = 256

n_label = 1+1

"""for roads"""
# model_save_path = '../../data/models/unet_roads.h5'
# train_data_path = '../../data/traindata/binary/roads/'

"""for buildings"""
model_save_path = '../../data/models/unet_buildings_onehot.h5'
train_data_path = '../../data/traindata/all/binary/buildings/'

def load_img(path, grayscale=False):
    if grayscale:
        img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
    else:
        img = cv2.imread(path)
        img = np.array(img, dtype="float") / 255.0  # MY image preprocessing
    return img

"""get the train file name and divide to train and val parts"""
def get_train_val(val_rate=0.25):
    train_url = []
    train_set = []
    val_set = []
    for pic in os.listdir(train_data_path + 'src'):
        train_url.append(pic)
    random.shuffle(train_url)
    total_num = len(train_url)
    val_num = int(val_rate * total_num)
    for i in range(len(train_url)):
        if i < val_num:
            val_set.append(train_url[i])
        else:
            train_set.append(train_url[i])
    return train_set, val_set

# data for training
def generateData(batch_size, data=[]):
    # print 'generateData...'
    while True:
        train_data = []
        train_label = []
        batch = 0
        for i in (range(len(data))):
            url = data[i]
            batch += 1
            img = load_img(train_data_path + 'src/' + url)

            # Adapt dim_ordering automatically
            img = img_to_array(img)
            train_data.append(img)
            label = load_img(train_data_path + 'label/' + url, grayscale=True)
            label = img_to_array(label)
            train_label.append(label)
            if batch % batch_size == 0:
                # print 'get enough bacth!\n'
                train_data = np.array(train_data)
                train_label = np.array(train_label)
                train_label = to_categorical(train_label, num_classes=n_label)
                train_label = train_label.reshape((batch_size, img_w * img_h, n_label))
                yield (train_data, train_label)
                train_data = []
                train_label = []
                batch = 0

# data for validation
def generateValidData(batch_size, data=[]):
    # print 'generateValidData...'
    while True:
        valid_data = []
        valid_label = []
        batch = 0
        for i in (range(len(data))):
            url = data[i]
            batch += 1
            img = load_img(train_data_path + 'src/' + url)

            # Adapt dim_ordering automatically
            img = img_to_array(img)
            valid_data.append(img)
            label = load_img(train_data_path + 'label/' + url, grayscale=True)
            label = img_to_array(label)
            valid_label.append(label)
            if batch % batch_size == 0:
                valid_data = np.array(valid_data)
                valid_label = np.array(valid_label)
                valid_label = to_categorical(valid_label, num_classes=n_label)
                valid_label = valid_label.reshape((batch_size, img_w * img_h, n_label))
                yield (valid_data, valid_label)
                valid_data = []
                valid_label = []
                batch = 0

def unet():
    # inputs = Input((3, img_w, img_h))  # channels_first
    inputs = Input((img_w, img_h, 3))

    conv1 = Conv2D(32, (3, 3), activation="relu", padding="same")(inputs)
    conv1 = Conv2D(32, (3, 3), activation="relu", padding="same")(conv1)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)

    conv2 = Conv2D(64, (3, 3), activation="relu", padding="same")(pool1)
    conv2 = Conv2D(64, (3, 3), activation="relu", padding="same")(conv2)
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)

    conv3 = Conv2D(128, (3, 3), activation="relu", padding="same")(pool2)
    conv3 = Conv2D(128, (3, 3), activation="relu", padding="same")(conv3)
    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)

    conv4 = Conv2D(256, (3, 3), activation="relu", padding="same")(pool3)
    conv4 = Conv2D(256, (3, 3), activation="relu", padding="same")(conv4)
    # pool4 = MaxPooling2D(pool_size=(2, 2))(conv4)
    drop4 = Dropout(0.5)(conv4) # add 20180621
    pool4 = MaxPooling2D(pool_size=(2, 2))(drop4)

    conv5 = Conv2D(512, (3, 3), activation="relu", padding="same")(pool4)
    conv5 = Conv2D(512, (3, 3), activation="relu", padding="same")(conv5)
    drop5 = Dropout(0.5)(conv5)

    # up6 = concatenate([UpSampling2D(size=(2, 2))(drop5), conv4], axis=1)
    up6 = concatenate([UpSampling2D(size=(2, 2))(drop5), conv4], axis=3)
    conv6 = Conv2D(256, (3, 3), activation="relu", padding="same")(up6)
    conv6 = Conv2D(256, (3, 3), activation="relu", padding="same")(conv6)

    # up7 = concatenate([UpSampling2D(size=(2, 2))(conv6), conv3], axis=1)
    up7 = concatenate([UpSampling2D(size=(2, 2))(conv6), conv3], axis=3)
    conv7 = Conv2D(128, (3, 3), activation="relu", padding="same")(up7)
    conv7 = Conv2D(128, (3, 3), activation="relu", padding="same")(conv7)

    # up8 = concatenate([UpSampling2D(size=(2, 2))(conv7), conv2], axis=1)
    up8 = concatenate([UpSampling2D(size=(2, 2))(conv7), conv2], axis=3)
    conv8 = Conv2D(64, (3, 3), activation="relu", padding="same")(up8)
    conv8 = Conv2D(64, (3, 3), activation="relu", padding="same")(conv8)

    # up9 = concatenate([UpSampling2D(size=(2, 2))(conv8), conv1], axis=1)
    up9 = concatenate([UpSampling2D(size=(2, 2))(conv8), conv1], axis=3)
    conv9 = Conv2D(32, (3, 3), activation="relu", padding="same")(up9)
    conv9 = Conv2D(32, (3, 3), activation="relu", padding="same")(conv9)

    conv10 = Conv2D(n_label, (1, 1), activation="sigmoid")(conv9)
    conv10 = Reshape((img_w * img_h, n_label))(conv10)  # 4D(bath_size, img_w*img_h, n_label)

    model = Model(inputs=inputs, outputs=conv10)
    model.compile(optimizer='Adam', loss='binary_crossentropy', metrics=['accuracy'])
    return model

""" For tes multiGPU model"""
import keras
from keras.utils import multi_gpu_model
class CustomModelCheckpoint(keras.callbacks.Callback):

    def __init__(self,  path):
        # self.model = model
        self.path = path
        self.best_loss = np.inf

    def on_epoch_end(self, epoch, logs=None):
        val_loss = logs['val_loss']
        if val_loss < self.best_loss:
            print("\nValidation loss decreased from {} to {}, saving model".format(self.best_loss, val_loss))

        self.model.save_weights(self.path, overwrite=True)

        self.best_loss = val_loss

def train():
    EPOCHS = 10  # should be 10 or bigger number
    BS = 16

    model = unet()

    """test the model fastly but can only train one epoch, AND it does not work finally ^_^"""
    # model = multi_gpu_model(model, gpus=4)
    # model.compile(optimizer=Adam(lr=1e-5), loss='binary_crossentropy', metrics=['accuracy'])
    ##### modelcheck = [CustomModelCheckpoint('./data/models/unet_fff.h5')]

    modelcheck = ModelCheckpoint(model_save_path, monitor='val_acc', save_best_only=True, mode='max')
    callable = [modelcheck]
    train_set, val_set = get_train_val()
    train_numb = len(train_set)
    valid_numb = len(val_set)
    print ("the number of train data is", train_numb)
    print ("the number of val data is", valid_numb)
    H = model.fit_generator(generator=generateData(BS, train_set), steps_per_epoch=train_numb // BS, epochs=EPOCHS,
                            verbose=1,
                            validation_data=generateValidData(BS, val_set), validation_steps=valid_numb // BS,
                            callbacks=callable, max_q_size=1)

    #plot the training loss and accuracy
    plt.style.use("ggplot")
    plt.figure()
    N = EPOCHS
    plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
    plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
    plt.plot(np.arange(0, N), H.history["acc"], label="train_acc")
    plt.plot(np.arange(0, N), H.history["val_acc"], label="val_acc")
    plt.title("Training Loss and Accuracy on U-Net Satellite Seg")
    plt.xlabel("Epoch #")
    plt.ylabel("Loss/Accuracy")
    plt.legend(loc="lower left")
    plt.savefig('../../data/models/unet_binary_train.png')

"""
only test
"""
window_size=256

def unet_predict(image,model):
    stride = window_size

    h, w, _ = image.shape
    print('h,w:', h, w)
    padding_h = (h // stride + 1) * stride
    padding_w = (w // stride + 1) * stride
    padding_img = np.zeros((padding_h, padding_w, 3))
    padding_img[0:h, 0:w, :] = image[:, :, :]

    padding_img = img_to_array(padding_img)

    mask_whole = np.zeros((padding_h, padding_w), dtype=np.float32)
    for i in list(range(padding_h // stride)):
        for j in list(range(padding_w // stride)):
            crop = padding_img[i * stride:i * stride + window_size, j * stride:j * stride + window_size, :3]

            crop = np.expand_dims(crop, axis=0)
            print('crop:{}'.format(crop.shape))

            # pred = model.predict_classes(crop, verbose=2)
            # pred = model.predict(crop, verbose=2)
            pred = model.predict(crop, verbose=2)
            pred = np.argmax(pred,axis=2)  #for one hot encoding

            pred = pred.reshape(256, 256)
            print(np.unique(pred))

            mask_whole[i * stride:i * stride + window_size, j * stride:j * stride + window_size] = pred[:, :]

    # outputresult = mask_whole[0:h, 0:w] * 255.0
    outputresult =mask_whole[0:h,0:w]
    outputresult = outputresult.astype(np.uint8)

    plt.imshow(outputresult, cmap='gray')
    plt.title("Original predicted result")
    plt.show()
    cv2.imwrite('../../data/predict/test_onehot.png',outputresult*255)
    return outputresult

if __name__ == '__main__':

    if not os.path.isdir(train_data_path):
        print ("train data does not exist in the path:\n {}".format(train_data_path))

    train()

    # print("test ....................predict by unet one hot encoding .....\n")
    # img_path = '../../data/test/1.png'
    # import sys
    #
    # if not os.path.isfile(img_path):
    #     print("no file: {}".forma(img_path))
    #     sys.exit(-1)
    #
    # input_img = cv2.imread(img_path)
    # input_img = np.array(input_img, dtype="float") / 255.0  # must do it
    #
    # new_model = load_model(model_save_path)
    #
    # unet_predict(input_img, new_model)


from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required

from .models import Post, Comment
from .forms import PostForm, CommentForm
from authuser.models import USER_GROUPS
from news_posts.models import POST_STATUSES

# Create your views here.
def	post_list(request):
    posts = Post.published.all()
    return render(request, 'post/list.html', {'posts': posts})

def	post_detail(request, year, month, day, slug):
    post = get_object_or_404(Post, slug=slug, status='APPROVE', publish__year=year, publish__month=month,
                             publish__day=day)
    comments = post.post_comments.filter(active=True)
    new_comment = None

    if request.method == "POST":
        if not request.user.is_authenticated:
            return redirect('authuser:signing_in')
        comment_form = CommentForm(data=request.POST)
        if comment_form.is_valid():
            new_comment = comment_form.save(commit=False)
            new_comment.author = request.user
            new_comment.post = post
            new_comment.save()
    else:
        comment_form = CommentForm()

    return render(request, 'post/detail.html', {'post': post, 'comments':comments, 'comment_form':comment_form})

@login_required(login_url='/auth/sign-in/')
def	post_create(request):
    post_form = PostForm()
    context_dictionary = {
        "post_form": post_form,
    }

    if request.method == "POST":
        post_form = PostForm(request.POST)
        if post_form.is_valid():
            new_post = post_form.save(commit=False)
            new_post.author = request.user
            if request.user.group != USER_GROUPS[0][0]:
                new_post.status = POST_STATUSES[1][0]
            new_post.save()
            return redirect('news_posts:post_list')

    return render(request, 'post/create.html', context_dictionary)


import pymc3 as pm
import numpy as np
import arviz as az
import matplotlib.pyplot as plt
x_size = 100
global x1,x2
x1 = np.random.randn(x_size)
x2 = np.random.randn(x_size)
def sim_model(a,b):
    #x_size = 10000
    #x1 = np.random.randn(x_size)
    #x2 = np.random.randn(x_size)
    return x1*a + x2*b +3
at = 3
bt = 5
y_obs = sim_model(at, bt)

with pm.Model() as my_model:
    a = pm.Uniform("a",lower=1,upper=4)
    b = pm.Uniform("b",lower=4,upper=7)
    s = pm.Simulator("s",sim_model,params=[a,b],sum_stat="sort", epsilon=1, observed=y_obs)

    trace, sim_data = pm.sample_smc(kernel="ABC", parallel=False, save_sim_data=True)
    idata = az.from_pymc3(trace, posterior_predictive=sim_data)

az.plot_trace(idata, kind="rank_vlines");
plt.show()


import json
import sys, os
import requests
import urllib, urllib3
import time
import threading
import cv2
import kivy

import cognitive_face as CF
import numpy as np

from kivy.app import App
from kivy.cache import Cache
from kivy.clock import Clock, mainthread
from kivy.graphics import *
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
from kivy.properties import ObjectProperty
from kivy.uix.image import AsyncImage
from kivy.core.window import Window
from kivy.graphics.texture import Texture
from PIL import Image, ImageDraw
from functools import partial

urllib3.disable_warnings()
kivy.require('1.10.0')

######################### Microsoft Cognitive ###############################
global mcg_group_name
mcg_group_name = "large-person-group-dev"
KEY = os.environ['COG_KEY']
CF.Key.set(KEY)
BASE_URL = 'https://westcentralus.api.cognitive.microsoft.com/face/v1.0/'  # Replace with your regional Base URL
CF.BaseUrl.set(BASE_URL)

#img_path = os.path.join(os.path.curdir, 'environment_image.png')
################### Initialize global variables ##############################

class SenseAdEndpoint():
    URL = "http://sensead.westcentralus.cloudapp.azure.com:8000"
    GET_ADS_PATH = "/getAdsForUser?user_id="
    RATE_AD_PATH = "/rateAd"
    LOG_OUT_PATH = "/logOut"

class CapturedFrame():
    def __init__(self, frame):
        self.frame = frame

    def read(self):
        png_image = cv2.imencode('.png', self.frame)[1]
        return bytes(bytearray(png_image.tostring()))

    def texture(self):
        buf = cv2.flip(self.frame, 0).tostring()
        image_texture = Texture.create(size=(self.frame.shape[1], self.frame.shape[0]), colorfmt='bgr')
        image_texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
        return image_texture

class ScreenOne(Screen):

    def __init__(self, **kwargs):
        super(ScreenOne, self).__init__(**kwargs)
        self.camera_thread = threading.Thread(target=self.acquireImage)
        self.wait = 3
        self.quit = True
        self.response_json = None
        self.ad_counter = 0
        self.timer_stop = 0
        self.payment = 0

    def spawn_camera_thread(self):
        self.camera_thread = threading.Thread(target=self.acquireImage)
        self.camera_thread.setDaemon(True)
        self.camera_thread.start()

    def on_enter(self, *args):
        self.set_init_widgets()
        self.spawn_camera_thread()

    def getRectangle(self, faceDictionary):
        rect = faceDictionary['faceRectangle']
        left = rect['left']
        top = rect['top']
        bottom = left + rect['height']
        right = top + rect['width']
        return ((left, top), (bottom, right))

    def acquireImage(self):
        # State global variables
        cap = cv2.VideoCapture(0)

        while True:
            # Code for waiting after frame (integer value to milli-seconds)
            time.sleep(self.wait)

            if self.quit == True:
                ret, frame = cap.read()

            # Check if frame was successfully acquired
            if ret == True:
                face_image = CapturedFrame(frame)

                faces = CF.face.detect(face_image)
                for face in faces:
                    identities = CF.face.identify([face['faceId']], large_person_group_id=mcg_group_name)[0]["candidates"]

                    if len(identities) > 0:
                        ad_request_url = SenseAdEndpoint.URL + SenseAdEndpoint.GET_ADS_PATH + identities[0]['personId']
                        contents = requests.get(ad_request_url)
                        self.response_json = contents.json()
                        self.quit = False
                        self.user_found(face, face_image)
                        cap.release()
                        return

    @mainthread
    def user_found(self, face, face_image):
        self.ad_counter = 0
        self.payment = 0
        userName = self.response_json["person"]["personName"]
        self.ids.user_label.color = 0, 1, 0, 1
        self.ids.user_label.text = "Hello, " + userName + '.'
        #img = Image.open(img_path)
        #img2 = img.crop((self.getRectangle(face)[0][0] - 40, self.getRectangle(face)[0][1] - 75,
        #                 self.getRectangle(face)[1][0] + 40, self.getRectangle(face)[1][1] + 30))
        #img2.save("temp_img2.png")
        #self.ids.user_image.source = ""
        #self.ids.user_image.source = "temp_img2.png"
        self.ids.user_image.texture = face_image.texture()
        #self.ids.user_image.reload()
        #self.ids.user_image.source = ""
        self.ids.center_image.source = self.response_json["ads"][self.ad_counter]["ad"]["url"]
        self.change_texts()

    @mainthread
    def help_popup(self):
        help_text = 'This is the SenseAd Kiosk. If you have registered through the app then the kiosk will be able to identify you. When prompted, select a category of ads to view. A timer will be on to indicate if you have viewed the ad for enough time (5 seconds).\n'
        button_text = 'Press to close me!'
        content = BoxLayout()
        self.popup_help = Popup(title='Help Page', content=content, auto_dismiss=True, size_hint=[.6,.3])

        self.label_help = Label(
            text=help_text,
            background_normal='',
            background_color=[0, 0, 0, 1],
            text_size_y=self.popup_help.size,
            padding_x=50,
            strip=True,
            valign="center",
            halign="left"
        )

        self.popup_help.bind(size=self.popup_update)
        content.add_widget(self.label_help)
        self.popup_help.content = content
        self.popup_help.open()

    def popup_update(self, *args):
        self.label_help.text_size = self.popup_help.size

    @mainthread
    def change_texts(self):
        self.ids.like_button.disabled = True
        self.ids.dislike_button.disabled = True
        self.ids.neutral_button.disabled = True
        self.ids.quit_button.disabled = False

        self.ids.quit_button.text = "Log Out"
        self.ids.quit_button.color = 1, 1, 1, 1
        self.ids.timer_label.text = "0s"
        self.ids.like_button.text = "Like"
        self.ids.like_button.color = 0, 1, 0, 0.5
        self.ids.dislike_button.text = "Dislike"
        self.ids.dislike_button.color = 1, 0, 0, 0.5
        self.ids.neutral_button.text = "Neutral"
        self.ids.neutral_button.color = 0, 0.75, 1, 0.5

        self.timer_stop=True
        self.disable_pref_buttons()
        self.start_ad_timer()

    @mainthread
    def set_init_widgets(self):
        self.timer_stop=False
        self.ids.timer_label.text = ""

        self.ids.help_button.bind(on_release=lambda *args: self.help_popup())
        self.ids.like_button.bind(on_press=lambda *ads: self.pref_ads('Like'))
        self.ids.dislike_button.bind(on_press=lambda *ads: self.pref_ads('Dislike'))
        self.ids.neutral_button.bind(on_press=lambda *ads: self.pref_ads('Neutral'))
        self.ids.quit_button.bind(on_press=lambda *args: self.begin_log_out())

        self.ids.like_button.disabled = True
        self.ids.dislike_button.disabled = True
        self.ids.neutral_button.disabled = True
        self.ids.quit_button.disabled = True

    @mainthread
    def enable_pref_buttons(self):
        self.ids.like_button.disabled = False
        self.ids.like_button.color = 0, 1, 0, 1
        self.ids.dislike_button.disabled = False
        self.ids.dislike_button.color = 1, 0, 0, 1
        self.ids.neutral_button.disabled = False
        self.ids.neutral_button.color = 0, 0.75, 1, 1

    @mainthread
    def disable_pref_buttons(self):
        self.ids.like_button.disabled = True
        self.ids.like_button.color = 0, 1, 0, 0.5
        self.ids.dislike_button.disabled = True
        self.ids.dislike_button.color = 1, 0, 0, 0.5
        self.ids.neutral_button.disabled = True
        self.ids.neutral_button.color = 0, 0.75, 1, 0.5

    @mainthread
    def timer_label_count(self, num, *args):
        if self.timer_stop == True:
            self.ids.timer_label.text = str(num)+'s'
            if num == 0:
                self.ids.timer_label.color = 1, 0, 0, 1
                self.enable_pref_buttons()
            else:
                self.ids.timer_label.color = 0, 1, 0, 1

    @mainthread
    def resize_screen(self):
        Window.fullscreen = 'auto'

    @mainthread
    def pref_ads(self, opin_ad):
        self.payment += 1
        r = requests.post(SenseAdEndpoint.URL + SenseAdEndpoint.RATE_AD_PATH, data={'user_id' : self.response_json["person"]["personId"], 'ad_id' : self.response_json["ads"][self.ad_counter]['ad_id'], 'rating': opin_ad})
        print(r.content)

        self.disable_pref_buttons()
        self.ad_counter += 1

        if (len(self.response_json["ads"]) > self.ad_counter):
            self.ids.center_image.source = self.response_json["ads"][self.ad_counter]["ad"]["url"]
            self.ids.center_image.reload()
            self.start_ad_timer()
        else:
            self.begin_log_out()

    def start_ad_timer(self):
        self.ids.timer_label.color = 0, 1, 0, 1
        time.sleep(0.5)

        for i in range(5, -1, -1):
            Clock.schedule_once(partial(self.timer_label_count, i), 5-i)

    @mainthread
    def popup_iota(self):
        popup_text = 'You received a payment of ' + str(self.payment) + ' IOTA.'
        content = Label( text=popup_text,
                        background_normal='',
                        background_color=[0, 0, 0, 1],
                        text_size=self.size,
                        halign='center',
                        valign='middle',
                        padding_x=50)
        popup = Popup(title='IOTA Payment', content=content, auto_dismiss=True,
                      size_hint=[.4, .2])
        content.bind(on_release=popup.dismiss)
        popup.bind(on_dismiss=self.quit_main)
        popup.open()

    def begin_log_out(self):
        self.popup_iota()

    @mainthread
    def quit_main(self, args):
        r = requests.post(SenseAdEndpoint.URL + SenseAdEndpoint.LOG_OUT_PATH, data={'user_id' : self.response_json["person"]["personId"], 'payment' : self.payment})
        self.ids.center_image.source = 'background.jpg'
        self.ids.user_image.source = 'background.jpg'
        self.ids.user_image.reload()

        self.ids.like_button.disabled = True
        self.ids.dislike_button.disabled = True
        self.ids.neutral_button.disabled = True
        self.ids.quit_button.disabled = True

        self.ids.quit_button.text = ""
        self.ids.quit_button.color = 0, 0, 0, 1
        self.ids.like_button.text = ""
        self.ids.like_button.color = 0, 0, 0, 1
        self.ids.dislike_button.text = ""
        self.ids.dislike_button.color = 0, 0, 0, 1
        self.ids.neutral_button.text = ""
        self.ids.neutral_button.color = 0, 0, 0, 1
        self.ids.timer_label.text = "    "
        self.ids.user_label.color = 1, 0, 0, 1
        self.ids.user_label.text = "Finding user..."

        self.timer_stop = False
        self.ids.timer_label.text = ""
        self.quit = True
        self.spawn_camera_thread()

class Manager(ScreenManager):
    stop = threading.Event()
    screen_one = ObjectProperty(None)

class ScreensApp(App):
    def on_stop(self):
        #self.root.stop.set()
        print("exited")

    # Start the program
    def build(self):
        m = Manager(transition=NoTransition())
        return m

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


import requests
from datetime import datetime
import os

APP_ID = os.environ["NT_APP_ID"]
API_KEY = os.environ["NT_API_KEY"]

headers = {
    "x-app-id": APP_ID,
    "x-app-key": API_KEY,
    "x-remote-user-id": "0"

}
exersise_param = {
    "query": input("Enter your Query: ")
}

exersise_endpoint = "https://trackapi.nutritionix.com/v2/natural/exercise"
sheety_endpoint = os.environ["SHEETY_ENDPOINT"]

response = requests.post(url=exersise_endpoint, json=exersise_param, headers=headers)
result = response.json()

today_date = datetime.now().strftime("%d/%m/%Y")
now_time = datetime.now().strftime("%X")

bearer_headers = {
    "Authorization": f"Bearer {os.environ['TOKEN']}"
}

for exercise in result["exercises"]:
    sheet_inputs = {
        "workout": {
            "date": today_date,
            "time": now_time,
            "exercise": exercise["name"].title(),
            "duration": exercise["duration_min"],
            "calories": exercise["nf_calories"]
        }
    }

    sheet_response = requests.post(sheety_endpoint, json=sheet_inputs,  headers=bearer_headers)

    print(sheet_response.text)


from creating_bot import bot, dp
from aiogram import types, Dispatcher
from aiogram.filters.command import Command
from aiogram import md, F
from aiogram.filters import Text
from datetime import datetime

@dp.message(Command('food'))
async def button_creation(message: types.Message):
    # тут мы типа создали заготовки для кнопок, их надписи
    kb = [[
        types.KeyboardButton(text="С пюрешкой"),
        types.KeyboardButton(text="Без пюрешки")
    ]]
    # кнопки которые отвечают
    keyboard = types.ReplyKeyboardRemove(
        keyboard=kb,
        resize_keyboard=True,
        input_field_placeholder='Выберите способ подачи',
        one_time_keyboard=True
    )
    await message.answer('Как подавать каклетки?', reply_markup=keyboard)

@dp.message(Text('С пюрешкой'))
async def with_puree(message: types.Message):
    await message.reply('Отличный выбор\! Сделаем с пюрешкой')

@dp.message(Text('Без пюрешки'))
async def without_puree(message: types.Message):
    await message.reply(f'{md.bold("Просто коклетка?")} Ну лан че\.\.\.')

# создали хендлер для любого текста в принципе
# получается, что message.text возвращает текст, хранимый в отправляемом сообщении
@dp.message(F.text)
async def time_sender(message: types.Message):
    time_now = datetime.now().strftime('%Y\-%m\-%d %H:%M')
    added_text = md.underline(f'Отправлено в {time_now}')

    if message.text.startswith('когда'):
        await message.answer(f'{message.text[5:]}\n\n{md.bold(added_text)}')
    else:

        data = {
            'email': 'None',
            'code': 'None',  # только code блять
            'url': 'None'
        }
        entities = message.entities
        if entities:  # это я сам дописал
            for item in entities:
                if item.type in data.keys():
                    data[item.type] = item.extract_from(message.text)

            await message.reply(
                "Here's what I found:\n\n"
                f"URL: {md.quote(data['url'])}\n"
                f"Password: {md.quote(data['code'])}\n"
                f"Email: {md.quote(data['email'])}\n"
            )

def handler_register_other(dp: Dispatcher):
    dp.message.register(button_creation, Command('food'))
    dp.message.register(with_puree, Text(''))