# -*- coding: utf-8 -*-
"""
@file
@brief Helpers to process blog post included in the documentation.
"""
import os
from io import StringIO
from contextlib import redirect_stdout, redirect_stderr
from docutils import io as docio
from docutils.core import publish_programmatically
from .._cst.cst_sphinx import get_epkg_dictionary

class BlogPostParseError(Exception):

    """
    Exception raised when a error comes after
    a blogpost was parsed.
    """
    pass

class BlogPost:

    """
    Defines a blog post.
    """

    def __init__(self, filename, encoding='utf-8-sig', raise_exception=False,
                 extensions=None, conf=None, **kwargs_overrides):
        """
        Creates an instance of a blog post from a file or a string.

        :param filename: filename or string
        :param encoding: encoding
        :param raise_exception: to raise an exception when the blog cannot
            be parsed
        :param extensions: list of extension to use to parse
            the content of the blog, if None, it will consider
            a default list (see @see cl BlogPost and
            @see fn get_default_extensions)
        :param conf: existing configuration
        :param kwargs_overrides: additional parameters for :epkg:`sphinx`

        The constructor creates the following members:

        * title
        * date
        * keywords
        * categories
        * _filename
        * _raw
        * rst_obj: the object generated by docutils (@see cl BlogPostDirective)
        * pub: Publisher

        Parameter *raise_exception* catches the standard error.
        Option `:process:` of command `.. runpython::` should be
        used within a blog post to avoid having the same process use
        sphinx at the same time.
        """
        if os.path.exists(filename):
            with open(filename, "r", encoding=encoding) as f:
                try:
                    content = f.read()
                except UnicodeDecodeError as e:  # pragma: no cover
                    raise RuntimeError(
                        'Unable to read filename (encoding issue):\n  '
                        'File "{0}", line 1'.format(filename)) from e
            self._filename = filename
        else:
            content = filename
            self._filename = None

        self._raw = content

        overrides = {}
        overrides["out_blogpostlist"] = []
        overrides["blog_background"] = True
        overrides["blog_background_page"] = False
        overrides["sharepost"] = None
        if conf is None or not getattr(conf, 'epkg_dictionary'):
            overrides['epkg_dictionary'] = get_epkg_dictionary()
        else:
            overrides['epkg_dictionary'] = conf.epkg_dictionary
        overrides.update(kwargs_overrides)

        overrides.update({  # 'warning_stream': StringIO(),
            'out_blogpostlist': [],
            'out_runpythonlist': [],
            'master_doc': 'stringblog'})

        if "extensions" not in overrides:
            if extensions is None:
                # To avoid circular references.
                from . import get_default_extensions
                extensions = get_default_extensions()
            overrides["extensions"] = extensions

        from ..helpgen.sphinxm_mock_app import MockSphinxApp
        app = MockSphinxApp.create(confoverrides=overrides)
        env = app[0].env
        config = env.config

        if 'blog_background' not in config:
            raise AttributeError(  # pragma: no cover
                "Unable to find 'blog_background' in config:\n{0}".format(
                    "\n".join(sorted(config.values))))
        if 'blog_background_page' not in config:
            raise AttributeError(  # pragma: no cover
                "Unable to find 'blog_background_page' in config:\n{0}".format(
                    "\n".join(sorted(config.values))))
        if 'epkg_dictionary' in config:
            if len(config.epkg_dictionary) > 0:
                overrides['epkg_dictionary'].update(config.epkg_dictionary)
            else:
                overrides['epkg_dictionary'].update(get_epkg_dictionary())

        env.temp_data["docname"] = "stringblog"
        overrides["env"] = env

        config.add('doctitle_xform', True, False, bool)
        config.add('initial_header_level', 2, False, int)
        config.add('input_encoding', encoding, False, str)

        keepout = StringIO()
        keeperr = StringIO()
        with redirect_stdout(keepout):
            with redirect_stderr(keeperr):
                _, pub = publish_programmatically(
                    source_class=docio.StringInput, source=content,
                    source_path=None, destination_class=docio.StringOutput, destination=None,
                    destination_path=None, reader=None, reader_name='standalone', parser=None,
                    parser_name='restructuredtext', writer=None, writer_name='null', settings=None,
                    settings_spec=None, settings_overrides=overrides, config_section=None,
                    enable_exit_status=None)

        all_err = keeperr.getvalue()
        if len(all_err) > 0:
            lines = all_err.strip(' \n\r').split('\n')
            lines = [_ for _ in lines
                     if ("in epkg_dictionary" not in _ and
                         "to be local relative or absolute" not in _)]
            std = keepout.getvalue().strip('\n\r\t ')
            if len(lines) > 0 and raise_exception:
                raise BlogPostParseError(  # pragma: no cover
                    "Unable to parse a blogpost:\n[sphinxerror]-F\n{0}"
                    "\nFILE\n{1}\nCONTENT\n{2}\n--OUT--\n{3}".format(
                        all_err, self._filename, content, keepout.getvalue()))
            if len(lines) > 0:
                print(all_err)
                if len(std) > 3:
                    print(std)
            else:
                for _ in all_err.strip(' \n\r').split('\n'):
                    print("    ", _)
                if len(std) > 3:
                    print(std)
            # we assume we just need the content, raising a warnings
            # might make some process fail later
            # warnings.warn("Raw rst was caught but unable to fully parse
            # a blogpost:\n[sphinxerror]-H\n{0}\nFILE\n{1}\nCONTENT\n{2}".format(
            #     all_err, self._filename, content))

        # document = pub.writer.document
        objects = pub.settings.out_blogpostlist

        if len(objects) != 1:
            raise BlogPostParseError(  # pragma: no cover
                f'no blog post (#={len(objects)}) in\n  File "{filename}", line 1')

        post = objects[0]
        for k in post.options:
            setattr(self, k, post.options[k])
        self.rst_obj = post
        self.pub = pub
        self._content = post.content

    def __cmp__(self, other):
        """
        This method avoids to get the following error
        ``TypeError: unorderable types: BlogPost() < BlogPost()``.

        @param      other       other @see cl BlogPost
        @return                 -1, 0, or 1
        """
        if self.Date < other.Date:
            return -1
        if self.Date > other.Date:
            return 1
        if self.Tag < other.Tag:
            return -1
        if self.Tag > other.Tag:
            return 1
        raise ValueError(  # pragma: no cover
            f"same tag for two BlogPost: {self.Tag}")

    def __lt__(self, other):
        """
        Tells if this blog should be placed before *other*.
        """
        if self.Date < other.Date:
            return True
        if self.Date > other.Date:
            return False
        if self.Tag < other.Tag:
            return True
        return False

    @property
    def Fields(self):
        """
        Returns the fields as a dictionary.
        """
        res = dict(title=self.title,
                   date=self.date,
                   keywords=self.Keywords,
                   categories=self.Categories)
        if self.BlogBackground is not None:
            res["blog_ground"] = self.BlogBackground
        if self.Author is not None:
            res["author"] = self.Author
        return res

    @property
    def Tag(self):
        """
        Produces a tag for the blog post.
        """
        return BlogPost.build_tag(self.Date, self.Title)

    @staticmethod
    def build_tag(date, title):
        """
        Builds the tag for a post.

        @param      date        date
        @param      title       title
        @return                 tag or label
        """
        return "post-" + date + "-" + \
               "".join([c for c in title.lower() if "a" <= c <= "z"])

    @property
    def FileName(self):
        """
        Returns the filename.
        """
        return self._filename

    @property
    def Title(self):
        """
        Returns the title.
        """
        return self.title

    @property
    def BlogBackground(self):
        """
        Returns the blog background or None if not defined.
        """
        return self.blog_ground if hasattr(self, "blog_ground") else None

    @property
    def Author(self):
        """
        Returns the author or None if not defined.
        """
        return self.author if hasattr(self, "author") else None

    @property
    def Date(self):
        """
        Returns the date.
        """
        return self.date

    @property
    def Year(self):
        """
        Returns the year, we assume ``self.date`` is a string like ``YYYY-MM-DD``.
        """
        return self.date[:4]

    @property
    def Keywords(self):
        """
        Returns the keywords.
        """
        return [_.strip() for _ in self.keywords.split(",")]

    @property
    def Categories(self):
        """
        Returns the categories.
        """
        return [_.strip() for _ in self.categories.split(",")]

    @property
    def Content(self):
        """
        Returns the content of the blogpost.
        """
        return self._content

    def post_as_rst(self, language, directive="blogpostagg", cut=False):
        """
        Reproduces the text of the blog post,
        updates the image links.

        @param      language    language
        @param      directive   to specify a different behavior based on
        @param      cut         truncate the post after the first paragraph
        @return                 blog post as RST
        """
        rows = []
        rows.append(f".. {directive}::")
        for f, v in self.Fields.items():
            if isinstance(v, str):
                rows.append(f"    :{f}: {v}")
            else:
                rows.append(f"    :{f}: {','.join(v)}")
        if self._filename is not None:
            spl = self._filename.replace("\\", "/").split("/")
            name = "/".join(spl[-2:])
            rows.append(f"    :rawfile: {name}")
        rows.append("")

        def can_cut(i, r, rows_stack):
            rs = r.lstrip()
            indent = len(r) - len(rs)
            if len(rows_stack) == 0:
                if len(rs) > 0:
                    rows_stack.append(r)
            else:
                indent2 = len(rows_stack[0]) - len(rows_stack[0].lstrip())
                last = rows_stack[-1]
                if len(last) > 0:
                    last = last[-1]
                if (indent == indent2 and len(rs) == 0 and
                        last in {'.', ';', ',', ':', '!', '?'}):
                    return True
                rows_stack.append(r)
            return False

        rows_stack = []
        if directive == "blogpostagg":
            for i, r in enumerate(self.Content):
                rows.append("    " + self._update_link(r))
                if cut and can_cut(i, r, rows_stack):
                    rows.extend(["", "    ..."])
                    break
        else:
            for i, r in enumerate(self.Content):
                rows.append("    " + r)
                if cut and can_cut(i, r, rows_stack):
                    rows.extend(["", "    ..."])
                    break

        rows.extend(["", ""])
        return "\n".join(rows)

    image_tag = ".. image:: "

    def _update_link(self, row):
        """
        Changes a link to an image if the page contains one into
        *year/img.png*.

        @param      row     row
        @return             new row
        """
        r = row.strip("\r\t ")
        if r.startswith(BlogPost.image_tag):
            i = len(BlogPost.image_tag)
            r2 = row[i:]
            if "/" in r2:
                return row
            row = f"{row[:i]}{self.Year}/{r2}"
            return row
        return row


"""

.. module:: driver
   :synopsis: manages the HTPolyNet application, provides the command-line interface entry point

.. moduleauthor: Cameron F. Abrams, <cfa22@drexel.edu>

"""
import logging
import os
import argparse as ap
import textwrap
import shutil
import glob
from .banner import banner, banner_message
from .runtime import Runtime,logrotate
from .command import Command
import HTPolyNet.projectfilesystem as pfs
import HTPolyNet.software as software
from .plot import plots
from .stringthings import my_logger
from .inputcheck import input_check
from .postsim import postsim
from .analyze import analyze

logger=logging.getLogger(__name__)

def info(args):
    """info handles the info subcommmand

    :param args: parsed arguments
    :type args: argparse.Namespace
    """
    print('This is some information on your installed version of HTPolyNet')
    l=pfs.lib_setup()
    software.sw_setup()
    print(l.info())
    print(software.to_string())
    possibles=l.get_example_names()
    print('Available examples using htpolynet fetch-example')
    for p in possibles:
        if p[0].isdecimal():
            print(f'   {p}')

def run(args):
    """run handles the run subcommand

    :param args: parsed arguments
    :type args: argparse.Namespace
    """
    logrotate(args.diag)
    ''' set up logger with all debug+-level messages going to the diagnostic log file and info to console '''
    loglevel_numeric=getattr(logging, args.loglevel.upper())
    logging.basicConfig(filename=args.diag,filemode='w',format='%(asctime)s %(name)s.%(funcName)s %(levelname)s> %(message)s',level=loglevel_numeric)
    console=logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter=logging.Formatter('%(levelname)s> %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)
    if not args.no_banner: banner(logger.info)
    my_logger('HTPolyNet runtime begins',logger.info)
    userlib=args.lib if os.path.exists(args.lib) else None
    software.sw_setup()
    pfs.pfs_setup(root=os.getcwd(),topdirs=['molecules','systems','plots'],verbose=True,projdir=args.proj,reProject=args.restart,userlibrary=userlib)
    a=Runtime(cfgfile=args.config,restart=args.restart)
    a.do_workflow(force_checkin=args.force_checkin,force_parameterization=args.force_parameterization)
    my_logger('HTPolyNet runtime ends',logger.info)

def parameterize(args):
    """parameterize handles the parameterize subcommand

    :param args: parsed arguments
    :type args: argparse.Namespace
    """
    logrotate(args.diag)
    ''' set up logger with all debug+-level messages going to the diagnostic log file and info to console '''
    loglevel_numeric=getattr(logging, args.loglevel.upper())
    logging.basicConfig(filename=args.diag,filemode='w',format='%(asctime)s %(name)s.%(funcName)s %(levelname)s> %(message)s',level=loglevel_numeric)
    console=logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter=logging.Formatter('%(levelname)s> %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)
    if not args.no_banner: banner(logger.info)
    my_logger('HTPolyNet parameterization begins',logger.info)
    userlib=args.lib
    if not os.path.exists(args.lib):
        userlib=None
    software.sw_setup()
    pfs.pfs_setup(root=os.getcwd(),topdirs=['molecules','systems','plots'],verbose=True,projdir=args.proj,reProject=args.restart,userlibrary=userlib)
    a=Runtime(cfgfile=args.config,restart=args.restart)
    a.generate_molecules(force_checkin=args.force_checkin,force_parameterization=args.force_parameterization)
    my_logger('HTPolynet parameterization ends',logger.info)

def fetch_example(args):
    """fetch_example handles the fetch-example subcommand

    :param args: parsed arguments
    :type args: argparse.Namespace
    """
    l=pfs.system()
    nm=args.n
    kp=args.k
    possibles=l.get_example_names()
    if nm.isdecimal():
        fullname=[x for x in possibles if x.startswith(nm)][0]
    else:
        if nm=='all':
            for fullname in possibles:
                shutil.copy(os.path.join(l.get_example_depot_location(),f'{fullname}.tgz'),'.')
                os.system(f'tar zxf {fullname}.tgz')
                if not kp:
                    os.remove(f'{fullname}.tgz')
            return
        fullname=nm
    fullpath=os.path.join(l.get_example_depot_location(),f'{fullname}.tgz')
    assert os.path.exists(fullpath),f'{fullpath}: not found.'
    shutil.copy(fullpath,'.')
    os.system(f'tar zxf {fullname}.tgz')
    if not kp:
        os.remove(f'{fullname}.tgz')

def pack_example(args):
    l=pfs.system()
    requires=['README.md','run.sh','lib/molecules']
    for r in requires:
        assert os.path.exists(r),f'{r}: not found.'
    ls=glob.glob('*.yaml')+glob.glob('*.yml')
    assert len(ls)>0,f'No yaml config file found.'
    bn=os.path.basename(os.getcwd())
    inspectname=bn.split('-')
    if len(inspectname)>0:
        firstfield=inspectname[0]
        if firstfield.isdigit():
            existing_n=int(firstfield)
            use_n=False
        else:
            use_n=True
    n=args.n
    existing_examples=l.get_example_names()
    numbers=[int(x.split('-')[0]) for x in existing_examples]
    assert not n in numbers,f'Choose an index other than {n}; an example with this index already exists.'
    if not use_n:
        assert not existing_n in numbers,f'Choose an index other than {n}; an example with this index already exists. To do this, rename the directory.'
    if n==-1:
        n=str(max(numbers)+1)
    if use_n:
        newname=f'{n}-{os.path.basename(os.getcwd())}'
    else:
        newname=f'{os.path.basename(os.getcwd())}'
    depot_location=l.get_example_depot_location()
    os.chdir('..')
    c=Command(f'tar --exclude="*/*/*/*/*" -zvcf {depot_location}/{newname}.tgz {bn}/README.md {bn}/run.sh {bn}/*.yaml {bn}/lib/molecules/')
    o,e=c.run()
    print(o)
    print(e)

def cli():
    """cli Command-line interface
    """
    l=pfs.lib_setup()
    example_names=l.get_example_names()
    example_ids=[x.split('-')[0] for x in example_names]
    # declare subcommands and their handlers
    commands={}
    commands['run']=run
    commands['parameterize']=parameterize
    commands['info']=info
    commands['plots']=plots
    commands['fetch-example']=fetch_example
    commands['input-check']=input_check
    commands['postsim']=postsim
    commands['analyze']=analyze
    commands['pack-example']=pack_example
    # declare subcommand helpstrings
    helps={}
    helps['run']='build a system using instructions in the config file and any required molecular structure inputs'
    helps['parameterize']='parameterize monomers and oligomer templates using instructions in the config file'
    helps['info']='print some information to the console'
    helps['plots']='generate some plots that summarize aspects of the current completed build'
    helps['fetch-example']='fetch and unpack example(s) from HTPolyNet.HTPolyNet.resources: '+', '.join([f'"{x}"' for x in l.get_example_names()])
    helps['pack-example']='pack current directory as a tarball to the resources/example_depot'
    helps['input-check']='reports number of atoms that would be in initial system based on config'
    helps['postsim']='perform specified postcure MD simulations on final results in one or more project directory'
    helps['analyze']='perform \'gmx <command>\' style analyses specified in the cfg file'
    parser=ap.ArgumentParser(description=textwrap.dedent(banner_message),formatter_class=ap.RawDescriptionHelpFormatter)
    # Subparsers, one per subcommand
    subparsers=parser.add_subparsers()
    subparsers.required=True
    command_parsers={}
    for k in commands:
        command_parsers[k]=subparsers.add_parser(k,help=helps[k])
        command_parsers[k].set_defaults(func=commands[k])
    ######## run ########
    command_parsers['run'].add_argument('config',type=str,default=None,help='input configuration file in YAML format')
    command_parsers['run'].add_argument('-lib',type=str,default='lib',help='local user library of molecular structures and parameterizations')
    command_parsers['run'].add_argument('-proj',type=str,default='next',help='project directory; "next" (default) generates next directory.  Anything other than "next": if it exists, "-restart" must be included as a parameter; if not, it is created as a new project')
    command_parsers['run'].add_argument('-diag',type=str,default='htpolynet_runtime_diagnostics.log',help='diagnostic log file')
    command_parsers['run'].add_argument('-restart',default=False,action='store_true',help='restart in latest proj dir')
    command_parsers['run'].add_argument('--no-banner',default=False,action='store_true',help='turn off the banner')
    command_parsers['run'].add_argument('--force-parameterization',default=False,action='store_true',help='force GAFF parameterization of any input mol2 structures')
    command_parsers['run'].add_argument('--force-checkin',default=False,action='store_true',help='force check-in of any generated parameter files to the system library')
    command_parsers['run'].add_argument('--loglevel',type=str,default='debug',help='Log level for messages written to diagnostic log (debug|info)')
    ######## parameterize ########
    command_parsers['parameterize'].add_argument('config',type=str,default=None,help='input configuration file in YAML format')
    command_parsers['parameterize'].add_argument('-lib',type=str,default='lib',help='local user library of molecular structures and parameterizations')
    command_parsers['parameterize'].add_argument('-proj',type=str,default='next',help='project directory; "next" (default) generates next directory.  Anything other than "next": if it exists, "-restart" must be included as a parameter; if not, it is created as a new project')
    command_parsers['parameterize'].add_argument('-diag',type=str,default='htpolynet_runtime_diagnostics.log',help='diagnostic log file')
    command_parsers['parameterize'].add_argument('-restart',default=False,action='store_true',help='restart in latest proj dir')
    command_parsers['parameterize'].add_argument('--force-parameterization',default=False,action='store_true',help='force GAFF parameterization of any input mol2 structures')
    command_parsers['parameterize'].add_argument('--force-checkin',default=False,action='store_true',help='force check-in of any generated parameter files to the system library')
    command_parsers['parameterize'].add_argument('--no-banner',default=False,action='store_true',help='turn off the banner')
    command_parsers['parameterize'].add_argument('--loglevel',type=str,default='debug',help='Log level for messages written to diagnostic log (debug|info)')
    ######## plots ########
    command_parsers['plots'].add_argument('source',type=str,choices=['diag','build','post'],default='build',help='source of data to plot')
    command_parsers['plots'].add_argument('--diags',type=str,default=[],nargs='+',help='names of diagnostic log files (1 or more)')
    command_parsers['plots'].add_argument('--proj',nargs='+',type=str,default=[],help='name of project director[y/ies]')
    command_parsers['plots'].add_argument('--cfg',type=str,nargs='+',default=[],help='name input config files')
    command_parsers['plots'].add_argument('--buildplot',type=str,nargs='+',default=['t'],choices=['t','g','n','c'],help='type of build plot to generate: t: traces (select using --traces); g: 2-D graph representations iteration by iteration; n: homo-N between crosslinks; c: cluster-size distributions')
    command_parsers['plots'].add_argument('--traces',type=str,nargs='+',default=['t','d','p'],choices=['t','d','p'],help='type of traces to plot from build: t: temperature; d: density; p: potential energy')
    command_parsers['plots'].add_argument('--n_points',type=int,nargs=2,default=[10,20],help='number of [cold-side,hot-side] data points in the Tg analysis to fit lines to')

    command_parsers['plots'].add_argument('--plotfile',type=str,default='',help='name of plot file to generate')
    command_parsers['plots'].add_argument('--no-banner',default=False,action='store_true',help='turn off the banner')
    command_parsers['plots'].add_argument('--loglevel',type=str,default='info',help='Log level for messages written to diagnostic log (debug|info)')
    ######## fetch-example ########
    command_parsers['fetch-example'].add_argument('-n',type=str,choices=example_ids+['all'],help='number of example tarball to unpack from '+', '.join(example_names))
    command_parsers['fetch-example'].add_argument('-k',default=False,action='store_true',help='keep tarballs')
    ######## pack-example
    command_parsers['pack-example'].add_argument('-n',type=int,default=-1,help='desired index (integer) for this example')
    ######## input-check ########
    command_parsers['input-check'].add_argument('config',type=str,default=None,help='input configuration file in YAML format')
    command_parsers['input-check'].add_argument('-lib',type=str,default='lib',help='local user library of molecular structures and parameterizations')
    ######## postsim ########
    command_parsers['postsim'].add_argument('-proj',type=str,default='',nargs='+',help='name of project directory')
    command_parsers['postsim'].add_argument('-lib',type=str,default='lib',help='local user library of molecular structures and parameterizations')
    command_parsers['postsim'].add_argument('-ocfg',type=str,default='',help='original HTPolyNet config file used to generate project(s)')
    command_parsers['postsim'].add_argument('-cfg',type=str,default='',help='config file for specifying the MD simulations to perform')
    command_parsers['postsim'].add_argument('--no-banner',default=False,action='store_true',help='turn off the banner')
    command_parsers['postsim'].add_argument('--loglevel',type=str,default='info',help='Log level for messages written to diagnostic log (debug|info)')

    ######## analyze ########
    command_parsers['analyze'].add_argument('-proj',type=str,default='',nargs='+',help='name of project directory')
    command_parsers['analyze'].add_argument('-lib',type=str,default='lib',help='local user library of molecular structures and parameterizations')
    command_parsers['analyze'].add_argument('-ocfg',type=str,default='',help='original HTPolyNet config file used to generate project(s)')
    command_parsers['analyze'].add_argument('-cfg',type=str,default='',help='config file for specifying the analyses to perform')
    command_parsers['analyze'].add_argument('--no-banner',default=False,action='store_true',help='turn off the banner')
    command_parsers['analyze'].add_argument('--loglevel',type=str,default='info',help='Log level for messages written to diagnostic log (debug|info)')
    args=parser.parse_args()
    args.func(args)


import cv2
import matplotlib.pyplot as plt

# resmi içe aktar
img = cv2.imread("img1.jpg")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.figure()
plt.imshow(img, cmap = "gray")
plt.axis("off")
plt.show()

# eşikleme
_, thresh_img = cv2.threshold(img, thresh = 60, maxval = 255, type = cv2.THRESH_BINARY)

plt.figure()
plt.imshow(thresh_img, cmap = "gray")
plt.axis("off")
plt.show()

# uyarlamalı eşik değeri
thresh_img2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11,8)
plt.figure()
plt.imshow(thresh_img2, cmap = "gray")
plt.axis("off")
plt.show()


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import logging
import os

import tensorflow.compat.v1 as tf
from keras import Model, Input
from keras.applications import MobileNet
from tensorflowjs.converters import load_keras_model

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class ModelConverter:
  """
    Creates a ModelConverter class from a TensorFlow.js model file.

    Args: :param config_json_path: Full filepath of weights manifest file
    containing the model architecture. :param weights_path_prefix: Full filepath
    to the directory in which the weights binaries exist. :param
    tflite_model_file: Name of the TFLite FlatBuffer file to be exported.
    :return: ModelConverter class.
  """

  def __init__(self, config_json_path, weights_path_prefix, tflite_model_file):
    self.config_json_path = config_json_path
    self.weights_path_prefix = weights_path_prefix
    self.tflite_model_file = tflite_model_file
    self.keras_model_file = 'merged.h5'

    # MobileNet Options
    self.input_node_name = 'the_input'
    self.image_size = 224
    self.alpha = 0.25
    self.depth_multiplier = 1
    self._input_shape = (1, self.image_size, self.image_size, 3)
    self.depthwise_conv_layer = 'conv_pw_13_relu'

  def convert(self):
    self.save_keras_model()
    self._deserialize_tflite_from_keras()
    logger.info('The TFLite model has been generated')
    self._purge()

  def save_keras_model(self):
    top_model = load_keras_model(
        self.config_json_path,
        self.weights_path_prefix,
        weights_data_buffers=None,
        load_weights=True,
        use_unique_name_scope=True)

    base_model = self.get_base_model()
    merged_model = self.merge(base_model, top_model)
    merged_model.save(self.keras_model_file)

    logger.info('The merged Keras HDF5 model has been saved as {}'.format(
        self.keras_model_file))

  def merge(self, base_model, top_model):
    """
        Merges base model with the classification block
        :return:  Returns the merged Keras model
        """
    logger.info('Initializing model...')

    layer = base_model.get_layer(self.depthwise_conv_layer)
    model = Model(inputs=base_model.input, outputs=top_model(layer.output))
    logger.info('Model created.')

    return model

  def get_base_model(self):
    """
        Builds MobileNet with the default parameters
        :return:  Returns the base MobileNet model
        """
    input_tensor = Input(shape=self._input_shape[1:], name=self.input_node_name)
    base_model = MobileNet(
        input_shape=self._input_shape[1:],
        alpha=self.alpha,
        depth_multiplier=self.depth_multiplier,
        input_tensor=input_tensor,
        include_top=False)
    return base_model

  def _deserialize_tflite_from_keras(self):
    converter = tf.lite.TFLiteConverter.from_keras_model_file(
        self.keras_model_file)
    tflite_model = converter.convert()

    with open(self.tflite_model_file, 'wb') as file:
      file.write(tflite_model)

  def _purge(self):
    logger.info('Cleaning up Keras model')
    os.remove(self.keras_model_file)


from flask import Flask
from flask_restful import Api,Resource,reqparse

from kuet_bus_test import get_data

app = Flask(__name__)
api = Api(app)
data =get_data()

class Bus_data(Resource):

    def get(self,id="bus"):
        if(id=="ALL" or id=="all"):
            return data,200
        for datac in data:
            for key in datac:
                if(key==id):
                    return datac[key],200
        return "Not Found",404

api.add_resource(Bus_data,"/data","/data/","/data/<string:id>")

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


import requests

#def nazwa_funkcji(argument1):

def policz_znaki(string):
	dlugosc_stringu = len(string)
	return dlugosc_stringu

#def policz_znaki(argument):
#	liczna_znakow = len(argument)
#	return liczba_znakow

link = 'https://zajecia-programowania-xd.pl/kubus_puchatek'
kubus_raw = requests.get(link)
kubus_text = kubus_raw.text #.encode('utf-8'))

#x = 'abc' #str
#y = 1 # int
#z = 0.0001 # float

#funkcje

#x = 'string'
#y = len(x)

#print(x)
#print(y)

#indekst
#link_b = link[0]
#link_b = link[10]
#link_b = link[::-1]

#metody wbudowane
#link_b = link.upper() #lower

#dodawanie ciagow znakow
#link_b = link[:21].upper()  + '    ' + link[10:]

#Dzielenie ciagow znakow
#split()

#ciag = 'abcd abcd abcd abcd'
ciag = 'tutaj sa jakies slowa'
efekt = ciag.split('a')

print()
print(link)
print(ciag)
print(efekt)
print()


# https://github.com/RainerKuemmerle/g2o/blob/master/g2o/examples/ba/ba_demo.cpp

import numpy as np
import g2o

from collections import defaultdict
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--noise', dest='pixel_noise', type=float, default=1.,
    help='noise in image pixel space (default: 1.0)')
parser.add_argument('--outlier', dest='outlier_ratio', type=float, default=0.,
    help='probability of spuroius observation  (default: 0.0)')
parser.add_argument('--robust', dest='robust_kernel', action='store_true', help='use robust kernel')
parser.add_argument('--dense', action='store_true', help='use dense solver')
parser.add_argument('--seed', type=int, help='random seed', default=0)
args = parser.parse_args()

def main():
    optimizer = g2o.SparseOptimizer()
    solver = g2o.BlockSolverSE3(g2o.LinearSolverEigenSE3())
    solver = g2o.OptimizationAlgorithmLevenberg(solver)
    optimizer.set_algorithm(solver)

    focal_length = (500, 500)
    principal_point = (320, 240)
    baseline = 0.075

    true_points = np.hstack([
        np.random.random((500, 1)) * 3 - 1.5,
        np.random.random((500, 1)) - 0.5,
        np.random.random((500, 1)) + 3])

    num_pose = 15
    for i in range(num_pose):
        # pose here means transform points from camera coordinates to world coordinates
        pose = g2o.SE3Quat(np.identity(3), [i*0.04-1, 0, 0])
        sbacam = g2o.SBACam(pose)
        sbacam.set_cam(*focal_length, *principal_point, baseline)

        v_cam = g2o.VertexCam()
        v_cam.set_id(i)
        v_cam.set_estimate(sbacam)
        if i < 2:
            v_cam.set_fixed(True)
        optimizer.add_vertex(v_cam)

    point_id = num_pose
    inliers = dict()
    sse = defaultdict(float)

    for i, point in enumerate(true_points):
        visible = []
        for j in range(num_pose):
            proj = optimizer.vertex(j).estimate().w2i.dot(
                np.hstack([point, [1]]))   # project to left camera
            z = proj[:2] / proj[2]
            if 0 <= z[0] < 640 and 0 <= z[1] < 480:
                visible.append((j, z))
        if len(visible) < 2:
            continue

        vp = g2o.VertexSBAPointXYZ()
        vp.set_id(point_id)
        vp.set_marginalized(True)
        vp.set_estimate(point + np.random.randn(3))
        optimizer.add_vertex(vp)

        inlier = True
        for j, z in visible:
            if np.random.random() < args.outlier_ratio:
                inlier = False
                z = np.random.random(2) * [640, 480]
            z += np.random.randn(2) * args.pixel_noise

            edge = g2o.EdgeProjectP2MC()   # if stereo, use EdgeProjectP2SC
            edge.set_vertex(0, vp)
            edge.set_vertex(1, optimizer.vertex(j))
            edge.set_measurement(z)
            edge.set_information(np.identity(2))
            if args.robust_kernel:
                edge.set_robust_kernel(g2o.RobustKernelHuber())

            #edge.set_parameter_id(0, 0)
            optimizer.add_edge(edge)

        if inlier:
            inliers[point_id] = i
            error = vp.estimate() - true_points[i]
            sse[0] += np.sum(error**2)
        point_id += 1

    print('num vertices:', len(optimizer.vertices()))
    print('num edges:', len(optimizer.edges()))

    print('Performing full BA:')
    optimizer.initialize_optimization()
    optimizer.set_verbose(True)
    optimizer.optimize(10)

    for i in inliers:
        vp = optimizer.vertex(i)
        error = vp.estimate() - true_points[inliers[i]]
        sse[1] += np.sum(error**2)

    print('\nRMSE (inliers only):')
    print('before optimization:', np.sqrt(sse[0] / len(inliers)))
    print('after  optimization:', np.sqrt(sse[1] / len(inliers)))

if __name__ == '__main__':
    if args.seed > 0:
        np.random.seed(args.seed)

    main()


from typing import Callable, Optional

import torch
import torch.nn as nn
from fairseq import utils
from fairseq.modules import LayerNorm
from fairseq.modules.fairseq_dropout import FairseqDropout
from fairseq.modules.quant_noise import quant_noise

from .multihead_attention import MultiheadAttention
from .droppath import DropPath

class TransformerMEncoderLayer(nn.Module):
    """
    Implements a Transformer-M Encoder Layer.
    """

    def __init__(
        self,
        embedding_dim: int = 768,
        ffn_embedding_dim: int = 3072,
        num_attention_heads: int = 8,
        dropout: float = 0.1,
        attention_dropout: float = 0.1,
        activation_dropout: float = 0.1,
        activation_fn: str = "relu",
        export: bool = False,
        q_noise: float = 0.0,
        qn_block_size: int = 8,
        init_fn: Callable = None,
        sandwich_ln: bool = False,
        droppath_prob: float = 0.0,
    ) -> None:
        super().__init__()

        if init_fn is not None:
            init_fn()

        # Initialize parameters
        self.embedding_dim = embedding_dim
        self.num_attention_heads = num_attention_heads
        self.attention_dropout = attention_dropout
        self.q_noise = q_noise
        self.qn_block_size = qn_block_size

        if droppath_prob > 0.0:
            self.dropout_module = DropPath(droppath_prob)
        else:
            self.dropout_module = FairseqDropout(
                dropout, module_name=self.__class__.__name__
            )

        self.activation_dropout_module = FairseqDropout(
            activation_dropout, module_name=self.__class__.__name__
        )

        # Initialize blocks
        self.activation_fn = utils.get_activation_fn(activation_fn)
        self.self_attn = self.build_self_attention(
            self.embedding_dim,
            num_attention_heads,
            dropout=attention_dropout,
            self_attention=True,
            q_noise=q_noise,
            qn_block_size=qn_block_size,
        )

        self.sandwich_ln = sandwich_ln

        # layer norm associated with the self attention layer
        self.self_attn_layer_norm = LayerNorm(self.embedding_dim, export=export)

        # layer norm associated with the self attention layer, sandwich
        self.self_attn_sandwich_layer_norm = LayerNorm(self.embedding_dim, export=export) if self.sandwich_ln else None

        self.fc1 = self.build_fc1(
            self.embedding_dim,
            ffn_embedding_dim,
            q_noise=q_noise,
            qn_block_size=qn_block_size,
        )
        self.fc2 = self.build_fc2(
            ffn_embedding_dim,
            self.embedding_dim,
            q_noise=q_noise,
            qn_block_size=qn_block_size,
        )

        # layer norm associated with the position wise feed-forward NN
        self.final_layer_norm = LayerNorm(self.embedding_dim, export=export)

        self.final_sandwich_layer_norm = LayerNorm(self.embedding_dim, export=export) if self.sandwich_ln else None

    def build_fc1(self, input_dim, output_dim, q_noise, qn_block_size):
        return quant_noise(nn.Linear(input_dim, output_dim), q_noise, qn_block_size)

    def build_fc2(self, input_dim, output_dim, q_noise, qn_block_size):
        return quant_noise(nn.Linear(input_dim, output_dim), q_noise, qn_block_size)

    def build_self_attention(
        self,
        embed_dim,
        num_attention_heads,
        dropout,
        self_attention,
        q_noise,
        qn_block_size,
    ):
        return MultiheadAttention(
            embed_dim,
            num_attention_heads,
            dropout=dropout,
            self_attention=True,
            q_noise=q_noise,
            qn_block_size=qn_block_size,
        )

    def forward(
        self,
        x: torch.Tensor,
        self_attn_bias: Optional[torch.Tensor] = None,
        self_attn_mask: Optional[torch.Tensor] = None,
        self_attn_padding_mask: Optional[torch.Tensor] = None,
    ):
        """
        LayerNorm is applied either before or after the self-attention/ffn
        modules similar to the original Transformer implementation.
        """
        # x: T x B x C
        residual = x
        if self.sandwich_ln:
            x = self.self_attn_layer_norm(x)
        x, attn = self.self_attn(
            query=x,
            key=x,
            value=x,
            attn_bias=self_attn_bias,
            key_padding_mask=self_attn_padding_mask,
            need_weights=False,
            attn_mask=self_attn_mask,
        )
        x = self.dropout_module(x)
        if self.sandwich_ln:
            x = self.self_attn_sandwich_layer_norm(x)
        x = residual + x
        if not self.sandwich_ln:
            x = self.self_attn_layer_norm(x)

        residual = x
        if self.sandwich_ln:
            x = self.final_layer_norm(x)
        x = self.activation_fn(self.fc1(x))
        x = self.activation_dropout_module(x)
        x = self.fc2(x)
        x = self.dropout_module(x)
        if self.sandwich_ln:
            x = self.final_sandwich_layer_norm(x)
        x = residual + x
        if not self.sandwich_ln:
            x = self.final_layer_norm(x)
        return x, attn


#!/usr/bin/env python

import argparse, torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
import numpy as np
from utils_network import *

class EncoderCNN(nn.Module):
    def __init__(self,
                 img_x=200,
                 img_y=200,
                 input_channels=1,
                 fc_hidden1=1024,
                 fc_hidden2=768,
                 drop_p=0.5,
                 CNN_embed_dim=512):
        super(EncoderCNN, self).__init__()

        self.img_x = img_x
        self.img_y = img_y
        self.CNN_embed_dim = CNN_embed_dim

        # CNN architechtures
        self.ch1, self.ch2, self.ch3, self.ch4 = 8, 16, 32, 64
        self.k1, self.k2, self.k3, self.k4 = (5, 5), (3, 3), (3, 3), (
            3, 3)  # 2d kernal size
        self.s1, self.s2, self.s3, self.s4 = (2, 2), (2, 2), (2, 2), (
            2, 2)  # 2d strides
        self.pd1, self.pd2, self.pd3, self.pd4 = (0, 0), (0, 0), (0, 0), (
            0, 0)  # 2d padding

        # conv2D output shapes
        self.conv1_outshape = conv2D_output_size((self.img_x, self.img_y),
                                                 self.pd1, self.k1,
                                                 self.s1)  # Conv1 output shape
        self.conv2_outshape = conv2D_output_size(self.conv1_outshape, self.pd2,
                                                 self.k2, self.s2)
        self.conv3_outshape = conv2D_output_size(self.conv2_outshape, self.pd3,
                                                 self.k3, self.s3)
        self.conv4_outshape = conv2D_output_size(self.conv3_outshape, self.pd4,
                                                 self.k4, self.s4)

        # fully connected layer hidden nodes
        self.fc_hidden1, self.fc_hidden2 = fc_hidden1, fc_hidden2
        self.drop_p = drop_p

        self.conv1 = nn.Sequential(
            nn.Conv2d(in_channels=input_channels,
                      out_channels=self.ch1,
                      kernel_size=self.k1,
                      stride=self.s1,
                      padding=self.pd1),
            nn.BatchNorm2d(self.ch1, momentum=0.01),
            nn.ReLU(inplace=True),
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(in_channels=self.ch1,
                      out_channels=self.ch2,
                      kernel_size=self.k2,
                      stride=self.s2,
                      padding=self.pd2),
            nn.BatchNorm2d(self.ch2, momentum=0.01),
            nn.ReLU(inplace=True),
        )

        self.conv3 = nn.Sequential(
            nn.Conv2d(in_channels=self.ch2,
                      out_channels=self.ch3,
                      kernel_size=self.k3,
                      stride=self.s3,
                      padding=self.pd3),
            nn.BatchNorm2d(self.ch3, momentum=0.01),
            nn.ReLU(inplace=True),
        )

        self.conv4 = nn.Sequential(
            nn.Conv2d(in_channels=self.ch3,
                      out_channels=self.ch4,
                      kernel_size=self.k4,
                      stride=self.s4,
                      padding=self.pd4),
            nn.BatchNorm2d(self.ch4, momentum=0.01),
            nn.ReLU(inplace=True),
        )

        self.drop = nn.Dropout(self.drop_p)
        self.pool = nn.MaxPool2d(2)
        self.fc1 = nn.Linear(
            self.ch4 * self.conv4_outshape[0] * self.conv4_outshape[1],
            self.fc_hidden1)
        self.fc2 = nn.Linear(
            self.fc_hidden1,
            self.CNN_embed_dim)  # output = CNN embedding latent variables

    def forward(self, x_3d):
        cnn_embed_seq = []
        for t in range(x_3d.size(1)):
            # CNNs
            x = self.conv1(x_3d[:, t, :, :, :])
            x = self.conv2(x)
            x = self.conv3(x)
            x = self.conv4(x)
            x = x.view(x.size(0), -1)  # flatten the output of conv
            x = self.drop(x)
            # FC layers
            x = F.relu(self.fc1(x))
            x = self.drop(x)
            x = self.fc2(x)
            cnn_embed_seq.append(x)

        # swap time and sample dim such that (sample dim, time dim, CNN latent dim)
        cnn_embed_seq = torch.stack(cnn_embed_seq, dim=0).transpose_(0, 1)
        # cnn_embed_seq: shape=(batch, time_step, input_size)

        return cnn_embed_seq

class DecoderRNN(nn.Module):
    def __init__(self,
                 CNN_embed_dim=512,
                 h_RNN_layers=1,
                 h_RNN=512,
                 h_FC_dim=256,
                 drop_p=0.5,
                 output_dim=6):
        super(DecoderRNN, self).__init__()

        self.RNN_input_size = 4 * CNN_embed_dim
        self.h_RNN_layers = h_RNN_layers  # RNN hidden layersssssss
        self.h_RNN = h_RNN  # RNN hidden nodes
        self.h_FC_dim = h_FC_dim
        self.drop_p = drop_p
        self.output_dim = output_dim

        self.LSTM = nn.LSTM(
            input_size=self.RNN_input_size,
            hidden_size=self.h_RNN,
            num_layers=self.h_RNN_layers,
            batch_first=
            True,  # input & output will has batch size as 1s dimension. e.g. (batch, time_step, input_size)
            dropout=self.drop_p)

        self.fc1 = nn.Linear(self.h_RNN, self.h_FC_dim)
        self.bn = nn.BatchNorm1d(self.h_FC_dim)
        self.fc2 = nn.Linear(self.h_FC_dim, self.output_dim)
        self.drop = nn.Dropout(self.drop_p)

    def forward(self, x1_init, x1, x2_init, x2, device):

        self.LSTM.flatten_parameters()

        RNN_out, (h_n, h_c) = \
            self.LSTM(torch.cat((x1_init, x1, x2_init, x2), 2), None)
        x = self.fc1(RNN_out)
        x = F.relu(x)
        x = self.drop(x)
        x = torch.tanh(self.fc2(x))

        return x

    def forward_single(self, x1_init, x1, x2_init, x2, h_nc, device):
        self.LSTM.flatten_parameters()
        RNN_out, h_nc_ = \
            self.LSTM(torch.cat((x1_init, x1, x2_init, x2), 2), h_nc)
        x = self.fc1(RNN_out)
        x = F.relu(x)
        x = self.drop(x)
        x = torch.tanh(self.fc2(x))
        return x, h_nc_

class DecoderFC(nn.Module):
    def __init__(self,
                 CNN_embed_dim=512,
                 FC_layer_nodes=[512, 512, 256],
                 drop_p=0.5,
                 output_dim=6):
        super(DecoderFC, self).__init__()

        self.FC_input_size = 4 * CNN_embed_dim
        self.FC_layer_nodes = FC_layer_nodes
        self.drop_p = drop_p
        self.output_dim = output_dim

        assert len(FC_layer_nodes) == 3

        self.fc1 = nn.Linear(self.FC_input_size, self.FC_layer_nodes[0])
        self.fc2 = nn.Linear(self.FC_layer_nodes[0], self.FC_layer_nodes[1])
        self.fc3 = nn.Linear(self.FC_layer_nodes[1], self.FC_layer_nodes[2])
        self.fc4 = nn.Linear(self.FC_layer_nodes[2], self.output_dim)
        self.drop = nn.Dropout(self.drop_p)

    def forward(self, x11, x12, x21, x22, device):

        x = self.fc1(torch.cat((x11, x12, x21, x22), -1))
        x = F.relu(x)
        x = self.drop(x)
        x = self.fc2(x)
        x = F.relu(x)
        x = self.drop(x)
        x = self.fc3(x)
        x = F.relu(x)
        x = self.drop(x)
        x = self.fc4(x)
        x = torch.tanh(x)

        return x


import pandas as pd
from typing import Tuple, List
from src.models.base import Base
from src.models.salary import Salary
from src.database.connection import get_engine, get_session

class Initialize:

    @staticmethod
    def create_database() -> Tuple[bool, str]:
        try:
            Base.metadata.create_all(get_engine())
            return True, "Tabelas criadas com sucesso."
        except BaseException as e:
            return False, str(e)

    @staticmethod
    def drop_database() -> Tuple[bool, str]:
        try:
            Base.metadata.drop_all(get_engine())
            return True, "Tabelas removidas com sucesso."
        except BaseException as e:
            return False, str(e)

    @staticmethod
    def salary_data(base_path: str) -> Tuple[bool, str]:
        success, message = True, "Carga de dados de salário finalizada com sucesso."
        session = get_session()

        try:
            dataset = pd.read_csv(f"{base_path}/data/ds_salaries.csv")
            insert: List[Salary] = []

            converter = {
                "experience_level": {
                    "MI": "Júnior",
                    "SE": "Pleno",
                    "EN": "Sênior",
                    "EX": "Especialista"
                },
                "employment_type": {
                    "FT": "Full-time",
                    "CT": "Contrato",
                    "PT": "Meio período",
                    "FL": "Freelancer"
                },
                "company_size": {
                    "L": "Grande",
                    "S": "Pequena",
                    "M": "Média"
                }
            }

            for i in range(0, len(dataset)):
                insert.append(Salary(
                    work_year=int(dataset["work_year"].values[i]),
                    experience_level=converter["experience_level"][dataset["experience_level"].values[i]],
                    employment_type=converter["employment_type"][dataset["employment_type"].values[i]],
                    job_title=dataset["job_title"].values[i],
                    salary_usd=dataset["salary_in_usd"].values[i],
                    employee_residence=dataset["employee_residence"].values[i],
                    remote_ratio=dataset["remote_ratio"].values[i],
                    company_location=dataset["company_location"].values[i],
                    company_size=converter["company_size"][dataset["company_size"].values[i]]
                ))

            session.add_all(insert)
            session.commit()

        except BaseException as e:
            session.rollback()
            success, message = False, str(e)

        finally:
            session.close()
            return success, message


# function - find social gaze time point
def find_socialgaze_timepoint(body_part_locs_merge, min_length, angle_thres):

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import scipy
    import string
    import warnings
    import pickle

    animal_names_unique = pd.unique(pd.DataFrame(body_part_locs_merge.keys()).iloc[:,0])
    nanimals = animal_names_unique.shape[0]
    body_parts_unique = pd.unique(pd.DataFrame(body_part_locs_merge.keys()).iloc[:,1])
    nbodies = body_parts_unique.shape[0]

    warnings.filterwarnings('ignore')
    eye_direction_merge = {}
    eye_contact_or_not_merge = {}
    look_at_face_or_not_merge = {}

    for ianimal in np.arange(0,nanimals,1):

        iname = animal_names_unique[ianimal]
        #
        if (iname == animal_names_unique[0]):
            iname_other = animal_names_unique[1]
        elif (iname == animal_names_unique[1]):
            iname_other = animal_names_unique[0]

        eye_dir_frames = []
        eye_contact_frames = []
        look_at_face_frames = []

        for iframe in np.arange(0,min_length,1):

            # define the position of the animal's face part
            #
            # fill in the nan gaps
            if (iframe > 0):
                meaneye_loc_old = meaneye_loc
                meantuft_loc_old = meantuft_loc
                mass_loc_old = mass_loc
                whiblz_loc_old = whiblz_loc
            #
            lefteye_loc = np.array(body_part_locs_merge[(iname,'leftEye')])[iframe,:]
            righteye_loc = np.array(body_part_locs_merge[(iname,'rightEye')])[iframe,:]
            lefttuft_loc = np.array(body_part_locs_merge[(iname,'leftTuft')])[iframe,:]
            righttuft_loc = np.array(body_part_locs_merge[(iname,'rightTuft')])[iframe,:]
            whiblz_loc = np.array(body_part_locs_merge[(iname,'whiteBlaze')])[iframe,:]
            mouth_loc = np.array(body_part_locs_merge[(iname,'mouth')])[iframe,:]
            #
            meaneye_loc = np.nanmean(np.vstack((lefteye_loc,righteye_loc)),axis=0)
            #
            if ((np.sum(np.isnan(meaneye_loc))>0)&(iframe>0)):
                meaneye_loc = meaneye_loc_old
            #
            meantuft_loc = np.nanmean(np.vstack((lefttuft_loc,righttuft_loc)),axis=0)
            if ((np.sum(np.isnan(meantuft_loc))>0)&(iframe>0)):
                meantuft_loc = meantuft_loc_old
            #
            mass_loc = np.nanmean(np.vstack((lefteye_loc,righteye_loc,lefteye_loc,righteye_loc,whiblz_loc,mouth_loc)),axis=0)
            if ((np.sum(np.isnan(mass_loc))>0)&(iframe>0)):
                mass_loc = mass_loc_old
            #
            if ((np.sum(np.isnan(whiblz_loc))>0)&(iframe>0)):
                whiblz_loc = whiblz_loc_old

            # examine whether this animal is looking at the other's eyes or face
            #
            # fill in the nan gaps
            if (iframe > 0):
                meaneye_loc_other_old = meaneye_loc_other
                meantuft_loc_other_old = meantuft_loc_other
                mass_loc_other_old = mass_loc_other
                whiblz_loc_other_old = whiblz_loc_other
            #
            lefteye_loc_other = np.array(body_part_locs_merge[(iname_other,'leftEye')])[iframe,:]
            righteye_loc_other = np.array(body_part_locs_merge[(iname_other,'rightEye')])[iframe,:]
            lefttuft_loc_other = np.array(body_part_locs_merge[(iname_other,'leftTuft')])[iframe,:]
            righttuft_loc_other = np.array(body_part_locs_merge[(iname_other,'rightTuft')])[iframe,:]
            whiblz_loc_other = np.array(body_part_locs_merge[(iname_other,'whiteBlaze')])[iframe,:]
            mouth_loc_other = np.array(body_part_locs_merge[(iname_other,'mouth')])[iframe,:]
            #
            meaneye_loc_other = np.nanmean(np.vstack((lefteye_loc_other,righteye_loc_other)),axis=0)
            if ((np.sum(np.isnan(meaneye_loc_other))>0)&(iframe>0)):
                meaneye_loc_other = meaneye_loc_other_old
            #
            meantuft_loc_other = np.nanmean(np.vstack((lefttuft_loc_other,righttuft_loc_other)),axis=0)
            if ((np.sum(np.isnan(meantuft_loc_other))>0)&(iframe>0)):
                meantuft_loc_other = meantuft_loc_other_old
            #
            mass_loc_other = np.nanmean(np.vstack((lefteye_loc_other,righteye_loc_other,lefteye_loc_other,righteye_loc_other,whiblz_loc_other,mouth_loc_other)),axis=0)
            if ((np.sum(np.isnan(mass_loc_other))>0)&(iframe>0)):
                mass_loc_other = mass_loc_other_old
            #
            if ((np.sum(np.isnan(whiblz_loc_other))>0)&(iframe>0)):
                whiblz_loc_other = whiblz_loc_other_old

            # define important vectors for defining head gaze direction
            # fill in the nan gaps
            if (iframe > 0):
                eyesight_dir_old = eyesight_dir
            #
            Vect1 = lefteye_loc - righteye_loc
            Vect2 = whiblz_loc - mouth_loc
            Vect3 = lefttuft_loc - lefteye_loc
            Vect4 = righttuft_loc - righteye_loc
            Vect5 = meantuft_loc - meaneye_loc
            try:
                Vect1 = Vect1 / scipy.linalg.norm(Vect1)
                Vect2 = Vect2 / scipy.linalg.norm(Vect2)
                Vect3 = Vect3 / scipy.linalg.norm(Vect3)
                Vect4 = Vect4 / scipy.linalg.norm(Vect4)
                Vect5 = Vect5 / scipy.linalg.norm(Vect5)
            except:
                Vect1 = Vect1 / np.nanmax(np.absolute(Vect1))
                Vect2 = Vect2 / np.nanmax(np.absolute(Vect2))
                Vect3 = Vect3 / np.nanmax(np.absolute(Vect3))
                Vect4 = Vect4 / np.nanmax(np.absolute(Vect4))
                Vect5 = Vect5 / np.nanmax(np.absolute(Vect5))
            eyesight_dir = np.cross(Vect1, Vect2)
            try:
                eyesight_dir = eyesight_dir / scipy.linalg.norm(eyesight_dir)
            except:
                eyesight_dir = eyesight_dir / np.nanmax(np.absolute(eyesight_dir))
            #
            if (np.dot(eyesight_dir, Vect5)>0):
                eyesight_dir = -eyesight_dir
            #
            if ((np.sum(np.isnan(eyesight_dir))>0)&(iframe>0)):
                eyesight_dir = eyesight_dir_old
            eye_dir_frames.append(eyesight_dir)

            # where left eye is looking
            # vector between body part
            vect_face_eye = mass_loc_other - meaneye_loc
            vect_eye_eye = meaneye_loc_other - meaneye_loc
            try:
                vect_face_eye = vect_face_eye / scipy.linalg.norm(vect_face_eye)
            except:
                vect_face_eye = vect_face_eye / np.nanmax(np.absolute(vect_face_eye))
            try:
                vect_eye_eye = vect_eye_eye / scipy.linalg.norm(vect_eye_eye)
            except:
                vect_eye_eye = vect_eye_eye / np.nanmax(np.absolute(vect_eye_eye))

            angle_face_eye =  np.arccos(np.clip(np.dot(eyesight_dir/np.linalg.norm(eyesight_dir), vect_face_eye/np.linalg.norm(vect_face_eye)), -1.0, 1.0))
            angle_eye_eye =  np.arccos(np.clip(np.dot(eyesight_dir/np.linalg.norm(eyesight_dir), vect_eye_eye/np.linalg.norm(vect_eye_eye)), -1.0, 1.0))

            eye_contact_thres = ((angle_eye_eye>0)&(angle_eye_eye<angle_thres))
            eye_lookface_thres = ((angle_face_eye>0)&(angle_face_eye<angle_thres))

            eye_contact_frames.append(np.int(eye_contact_thres))
            look_at_face_frames.append(np.int(eye_lookface_thres))

        # save to the summarized data
        eye_direction_merge[(iname)] = eye_dir_frames
        eye_contact_or_not_merge[(iname)] = eye_contact_frames
        look_at_face_or_not_merge[(iname)] = look_at_face_frames

    return eye_direction_merge, eye_contact_or_not_merge, look_at_face_or_not_merge


from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .forms import UserRegisterForm, UserUpdateForm, ProfileUpdateForm
from issue.models import Issue
from django.http import JsonResponse
from datetime import date, timedelta

def register(request):
    if request.method== 'POST':
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            form.save()
            username=form.cleaned_data.get('username')
            messages.success(request, f'Account created for {username}! You can log in now.')
            return redirect('issue-home')
    else:
        form = UserRegisterForm()
    return render(request, 'users/register.html', {'form':form})

@login_required
def update_profile(request):
    if request.method == 'POST':
        u_form = UserUpdateForm(request.POST, instance=request.user)
        p_form =ProfileUpdateForm(request.POST,
                                  request.FILES,
                                  instance=request.user.profile)
        #instance used to populate field with existing data
        if u_form.is_valid() and p_form.is_valid():
            u_form.save()
            p_form.save()
            messages.success(request, f'Your account has been updated')
            return redirect('profile')

    else:
        u_form = UserUpdateForm(instance=request.user)
        p_form =ProfileUpdateForm(instance=request.user.profile)
    context = {
        'u_form': u_form,
        'p_form': p_form
    }
    return render(request, 'users/update_profile.html', context)

@login_required
def profile(request):
    return render(request, 'users/profile.html')

@login_required
def dashboard(request):
    high_posts=request.user.issue_set.filter(completed=False).filter(priority='high')[:4]
    medium_posts=request.user.issue_set.filter(completed=False).filter(priority='medium')[:4]
    low_posts=request.user.issue_set.filter(completed=False).filter(priority='low')[:4]

    context={
        'high_posts': high_posts,
        'medium_posts': medium_posts,
        'low_posts':low_posts,
    }
    return render(request,'users/dashboard.html', context)

def result_data(request):
    issue = request.user.issue_set.all()
    num_high = issue.filter(priority='high').count()
    num_medium = issue.filter(priority='medium').count()
    num_low = issue.filter(priority='low').count()

    priority_data=[
        {'high': num_high},
        {'medium': num_medium},
        {'low': num_low},
    ]

    return JsonResponse(priority_data, safe=False)

def finished_data(request):
    date_today= date.today()
    date_old=date_today - timedelta(7)
    issue = request.user.issue_set.filter(date_created__date__gte=date_old, date_created__date__lte=date_today)
    unfinished= issue.filter(completed=False).count()
    finished = issue.filter(completed=True).count()

    finished=[
        {'unfinished': unfinished},
        {'finished': finished},
    ]
    return JsonResponse(finished, safe=False)


# -*- coding: utf-8 -*-
import numpy as np
import torch
import random
from collections import deque, namedtuple

class ReplayBuffer:
    """Fixed-size buffer to store experience tuples."""

    def __init__(self, buffer_size, batch_size, num_agents, seed, device):
        """Initialize a ReplayBuffer object.
        Params
        ======
            buffer_size (int): maximum size of buffer
            batch_size (int): size of each training batch
        """
        self.memory = deque(maxlen=buffer_size)  # internal buffer (deque)
        self.batch_size = batch_size
        self.num_agents = num_agents
        self.experience = namedtuple(
            "Experience",
            field_names=["states", "actions", "rewards", "next_states", "dones"],
        )
        self.seed = random.seed(seed)
        self.device = device

    def store(self, state, action, reward, next_state, done):
        """Add a new experience to buffer."""
        experience = self.experience(state, action, reward, next_state, done)
        self.memory.append(experience)

    def sample(self):
        """Randomly sample a batch of experiences from buffer."""
        experiences = random.sample(self.memory, k=self.batch_size)

        states_list = [
            torch.from_numpy(
                np.vstack([e.states[index] for e in experiences if e is not None])
            )
            .float()
            .to(self.device)
            for index in range(self.num_agents)
        ]
        actions_list = [
            torch.from_numpy(
                np.vstack([e.actions[index] for e in experiences if e is not None])
            )
            .float()
            .to(self.device)
            for index in range(self.num_agents)
        ]
        next_states_list = [
            torch.from_numpy(
                np.vstack([e.next_states[index] for e in experiences if e is not None])
            )
            .float()
            .to(self.device)
            for index in range(self.num_agents)
        ]
        rewards = (
            torch.from_numpy(
                np.vstack([e.rewards for e in experiences if e is not None])
            )
            .float()
            .to(self.device)
        )
        dones = (
            torch.from_numpy(
                np.vstack([e.dones for e in experiences if e is not None]).astype(
                    np.uint8
                )
            )
            .float()
            .to(self.device)
        )

        return states_list, actions_list, rewards, next_states_list, dones

    def __len__(self):
        """Return the current size of internal memory."""
        return len(self.memory)


import sys
import pygame
import random
from pygame.sprite import Sprite
from ship import Ship
from alien import Alien
from alien_2 import Alien_2
from bullet import Bullet
from explosion import Explosion
from settings import Settings

def start_menu_off(aigame):
    aigame.background = pygame.image.load(
        'E:\\Programming\\Projects\\Alien Invasion\\images\\stars.jpg')
    aigame.background_rect = aigame.background.get_rect()

class AlienInvasion:
    """Main game class that manages game behaviour."""

    def __init__(self):
        """Initialises the main game attributes."""
        pygame.init()
        pygame.font.init()
        self.game_font = pygame.font.Font(None, 30)
        # Creates settings instance.
        self.settings = Settings()
        # Initialises screen.
        self.screen = pygame.display.set_mode(
            (self.settings.screen_width, self.settings.screen_height))
        self.screen_rect = self.screen.get_rect()
        # creates ship, alien and explosion instances.
        self.ship = Ship(self)
        self.alien = Alien(self)
        self.alien_2 = Alien_2(self)
        # Creates sprite groups.
        self.bullets = pygame.sprite.Group()
        self.aliens = pygame.sprite.Group()
        self.aliens_2 = pygame.sprite.Group()
        self.explosions = pygame.sprite.Group()
        # Background.
        self.background = pygame.image.load(
            'E:\\Programming\\Projects\\Alien Invasion\\images\\start_screen.jpg')
        self.background_rect = self.background.get_rect()
        self.background_rect.center = self.screen_rect.center
        # Other Attributes.
        self.max_bullets = self.settings.max_bullets
        # Timer Attributes for Ship and Alien animation.
        self.timer = 0
        self.animation_timer = 0
        self.current_image = 1
        self.current_explosion = 1
        # Amount of time the game waits before spawning aliens after start screen.
        self.game_start_timer = 0
        self.alien_timer = random.randint(
            self.settings.alien_min_time, self.settings.alien_max_time)
        # Score counter
        self.score = 0

        # Start game after start screen

        self.game_on = 0

    def run_game(self):
        """Starts the game loop."""
        while True:
            self._check_events()
            self._update_screen()
            self._check_collisions()

    def _animate_image(self):
        """Changes images of Ship, Alien and Explosions to show illusion
         of animation."""
        # Increment timer
        self.timer += 1
        self.animation_timer += 1
        self.game_start_timer += 1
        if self.timer == self.alien_timer:
            self.spawn_alien()
            self._randomise_spawn()
            self.timer = 0
        if self.animation_timer == 200:
            if self.current_image == 1:
                self.ship.image = pygame.image.load(
                    'E:\\Programming\\Projects\\Alien Invasion\\images\\player_idle2.png')
                if self.settings.logs == 2:
                    print(f"Set Alien and ship images to 2.")
                for a in self.aliens:
                    a.image = pygame.image.load(
                        'E:\\Programming\\Projects\\Alien Invasion\\images\\Alien1_2.png')
                self.current_image = 2
                self.animation_timer = 0
                # if self.settings.logs == 1:
                #    print("Changed Ship and Alien images to 2")
            else:
                self.ship.image = pygame.image.load(
                    'E:\\Programming\\Projects\\Alien Invasion\\images\\player_idle1.png')
                if self.settings.logs == 2:
                    print(f"Set Alien and ship images to 1.")
                for a in self.aliens:
                    a.image = pygame.image.load(
                        'E:\\Programming\\Projects\\Alien Invasion\\images\\Alien1_1.png')
                self.current_image = 1
                self.animation_timer = 0
                # if self.settings.logs == 1:
                #    print("Changed Ship and Alien images to 1")
        # Animate explosions
        """The following block uses timers and a boolean to animate to the 1st,
         2nd, 3rd image of explosion. Then back to
         2nd and 1st again, before deleting itslef."""
        if self.animation_timer % 50 == 0:
            for e in self.explosions:
                if e.current_image == 1:
                    if self.settings.logs > 2:
                        print(f"Set {e} image to 2.")
                    e.image = pygame.image.load(
                        'E:\\Programming\\Projects\\Alien Invasion\\images\\boom2.PNG')
                    if e.first_pass == True:
                        e.current_image = 2
                elif e.current_image == 2:
                    e.image = pygame.image.load(
                        'E:\\Programming\\Projects\\Alien Invasion\\images\\boom3.PNG')
                    if self.settings.logs > 2:
                        print(f"Set {e} image to 3.")
                    if e.first_pass == True:
                        e.current_image = 3
                else:
                    e.image = pygame.image.load(
                        'E:\\Programming\\Projects\\Alien Invasion\\images\\boom1.PNG')
                    if self.settings.logs > 2:
                        print(f"Set {e} image to 1.")
                        e.current_image = 1
                    else:
                        self.explosions.remove(e)

    def _check_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if self.game_on == 0:
                    self.game_on = 1
                    start_menu_off(self)

    def _check_keydown_events(self, event):
        if event.key == pygame.K_RIGHT:
            # Move the ship to the right.
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            # Move the ship to the right.
            self.ship.moving_left = True
        elif event.key == pygame.K_SPACE:
            # Fires bulets
            self._fire_bullets()
            if self.game_on == 0:
                self.game_on = 1
                start_menu_off(self)

    def _check_keyup_events(self, event):
        if event.key == pygame.K_RIGHT:
            # Stop moving the ship to the right.
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            # Stop moving the ship to the left.
            self.ship.moving_left = False
        elif event.key == pygame.K_q:
            # Quit game if 'q' is pressed
            sys.exit()

    def _update_bullets(self):
        """Updates position of bullets and deletes old bullets."""
        # Removes bullets off screen
        for bullet in self.bullets:
            if bullet.rect.bottom <= 0:
                self.bullets.remove(bullet)

        # Updates bullets on screen.
        self.bullets.update()
        for b in self.bullets:
            b.draw_bullet()

    def _update_alien(self):
        """Draws each alien."""
        # Deletes alien when it gets to the bottom of the screen
        for a in self.aliens:
            if a.rect.top > self.screen_rect.bottom:
                self.aliens.remove(a)
        # Draws and updates each alien individually
        for a in self.aliens:
            a.image = pygame.transform.scale(
                a.image, self.settings.alien_scale)
            a.update_alien()
        #    a._blitme()

    def _fire_bullets(self):
        new_bullet = Bullet(self)
        if len(self.bullets) < self.settings.max_bullets:
            self.bullets.add(new_bullet)
            if self.settings.logs >= 1:
                print(f"Bullet Fired! Total bullets : {len(self.bullets)}")

    def spawn_alien(self):
        new_alien = Alien(self)
        if len(self.aliens) < self.settings.max_aliens and self.game_start_timer > 1000:
            self.aliens.add(new_alien)
            if self.settings.logs >= 1:
                print(f"Alien spawned! Toal aliens : {len(self.aliens)}")

    def _check_collisions(self):
        """Checks to see if X and Y coordinates of any bullets collide with Aliens."""
        for bullet in self.bullets:
            for a in self.aliens:
                if bullet.rect.top > a.rect.top and bullet.rect.top < a.rect.bottom and bullet.rect.centerx > a.rect.left and bullet.rect.centerx < a.rect.right:
                    self._alien_hit(self, bullet, a)
                    self.aliens.remove(a)
                    self.bullets.remove(bullet)
                    self.score += 1

    def _alien_hit(self, aigame, bullet, a):
        """Registers the hit and calls the explosion animation."""
        new_explosion = Explosion(self, bullet, a)
        self.explosions.add(new_explosion)

    def _draw_explosions(self):
        """Draws the explosion images."""
        for explosion in self.explosions:
            explosion._draw_explosion()

    def _randomise_spawn(self):
        """Randomises the value the timer needs to reach before spawning another alien."""
        self.alien_timer = random.randint(
            self.settings.alien_min_time, self.settings.alien_max_time)

    def _draw_score(self):
        """Draws the score on the top left."""
        score_text = self.game_font.render(
            f"Score : {str(self.score)}", True, (255, 255, 255))
        score_rect = score_text.get_rect()
        score_rect.x, score_rect.y = self.screen_rect.right - \
            (score_rect.right - score_rect.left) - 10, 5
        self.screen.blit(score_text, score_rect)

    def _check_for_level_up(self):
        """Compares score to that of level up requirements, and modifies game accordingly."""
        if self.score >= self.settings.level_1up_score:
            self._levelup(1)

    def _levelup(self, level):
        """Changes game behaviour for a levelup."""
        if level == 1:  # Current level
            """Spawn alien2."""
            new_alien_2 = Alien_2(self)
            if len(self.aliens) < self.settings.max_aliens and self.game_start_timer > 1000:
                self.aliens_2.add(new_alien_2)

    def _update_screen(self):
        """Updates the screen with new info."""
        self.screen.fill(self.settings.bg_color)
        self.screen.blit(self.background, self.background_rect)
        # Draws images to screen
        if self.game_on == 1:
            # self._check_for_level_up()
            self.ship.blitme()
            self.ship.update(self)
            self._update_bullets()
            self._update_alien()
            self.aliens.draw(self.screen)
            self._draw_explosions()
            self._animate_image()
            self._draw_score()
        # Flips the newest drawn screen for the old one.
        pygame.display.flip()

if __name__ == '__main__':
    ai = AlienInvasion()
    ai.run_game()


from __future__ import annotations

import asyncio
import logging
import os
import re
from collections import Counter
from typing import TYPE_CHECKING, Callable

import aiozipkin
import pytest
import sentry_sdk
from aiozipkin.transport import StubTransport

import walnats

from .helpers import UDPLogProtocol, fuzzy_match_counter, get_random_name

if TYPE_CHECKING:
    from _pytest.logging import LogCaptureFixture

async def noop(msg: str) -> None:
    pass

async def explode(msg: str) -> None:
    1 / 0

SENTRY_DSN = os.environ.get('SENTRY_DSN')
CI = bool(os.environ.get('CI'))

class MockMiddleware(walnats.middlewares.Middleware):
    def __init__(self) -> None:
        self.hist: list[str] = []

    def on_start(self, ctx) -> None:
        self.hist.append('on_start')

    def on_success(self, ctx) -> None:
        self.hist.append('on_success')

    def on_failure(self, ctx) -> None:
        self.hist.append('on_failure')

async def run_actor(
    handler: Callable,
    messages: str | list[str],
    *middlewares: walnats.middlewares.Middleware,
    trace_id: str | None = None,
    **kwargs,
) -> None:
    event = walnats.Event(get_random_name(), str)
    actor = walnats.Actor(get_random_name(), event, handler, middlewares=middlewares)
    events_reg = walnats.Events(actor.event)
    actors_reg = walnats.Actors(actor)
    if isinstance(messages, str):
        messages = [messages]
    async with events_reg.connect() as pub_conn, actors_reg.connect() as sub_conn:
        await pub_conn.register()
        await sub_conn.register()
        await asyncio.gather(
            *[pub_conn.emit(event, m, trace_id=trace_id) for m in messages],
        )
        await asyncio.sleep(.01)
        await sub_conn.listen(burst=True, batch=len(messages), **kwargs)

async def test_custom_sync() -> None:
    triggered = []

    class Middleware(walnats.middlewares.Middleware):
        def on_failure(self, ctx: walnats.types.ErrorContext) -> None:
            triggered.append('on_failure')

        def on_start(self, ctx: walnats.types.Context) -> None:
            triggered.append('on_start')

        def on_success(self, ctx: walnats.types.OkContext) -> None:
            triggered.append('on_success')

    async def handler(msg: str) -> None:
        assert msg == 'hi'
        triggered.append('handler')

    await run_actor(handler, 'hi', Middleware())
    assert triggered == ['on_start', 'handler', 'on_success']

async def test_custom_sync__on_failure() -> None:
    triggered = []

    class Middleware(walnats.middlewares.Middleware):
        def on_failure(self, ctx: walnats.types.ErrorContext) -> None:
            triggered.append('on_failure')

        def on_start(self, ctx: walnats.types.Context) -> None:
            triggered.append('on_start')

        def on_success(self, ctx: walnats.types.OkContext) -> None:
            triggered.append('on_success')

    async def handler(msg: str) -> None:
        assert msg == 'hi'
        triggered.append('handler')
        raise ZeroDivisionError

    await run_actor(handler, 'hi', Middleware())
    assert triggered == ['on_start', 'handler', 'on_failure']

async def test_custom_async() -> None:
    triggered = []

    class Middleware(walnats.middlewares.Middleware):
        async def on_failure(self, ctx: walnats.types.ErrorContext) -> None:
            triggered.append('on_failure')
            raise ctx.exception

        async def on_start(self, ctx: walnats.types.Context) -> None:
            triggered.append('on_start')

        async def on_success(self, ctx: walnats.types.OkContext) -> None:
            triggered.append('on_success')

    async def handler(msg: str) -> None:
        assert msg == 'hi'
        triggered.append('handler')

    await run_actor(handler, 'hi', Middleware())
    assert len(triggered) == 3
    assert set(triggered) == {'on_start', 'handler', 'on_success'}

async def test_custom_async__on_failure() -> None:
    triggered = []

    class Middleware(walnats.middlewares.Middleware):
        async def on_failure(self, ctx: walnats.types.ErrorContext) -> None:
            triggered.append('on_failure')

        async def on_start(self, ctx: walnats.types.Context) -> None:
            triggered.append('on_start')

        async def on_success(self, ctx: walnats.types.OkContext) -> None:
            triggered.append('on_success')

    async def handler(msg: str) -> None:
        assert msg == 'hi'
        triggered.append('handler')
        raise ZeroDivisionError

    await run_actor(handler, 'hi', Middleware())
    assert len(triggered) == 3
    assert set(triggered) == {'on_start', 'handler', 'on_failure'}

async def test_ExtraLogMiddleware(caplog: LogCaptureFixture) -> None:
    caplog.set_level(logging.DEBUG)
    await run_actor(noop, 'hi', walnats.middlewares.ExtraLogMiddleware())
    records = []
    for record in caplog.records:
        if record.name.startswith('walnats'):
            records.append(record)
    assert len(records) == 2
    assert records[0].message == 'event received'
    assert records[1].message == 'event processed'

async def test_ExtraLogMiddleware__on_failure(caplog: LogCaptureFixture) -> None:
    caplog.set_level(logging.DEBUG)
    await run_actor(explode, 'hi', walnats.middlewares.ExtraLogMiddleware())
    records = []
    for record in caplog.records:
        if record.name.startswith('walnats'):
            records.append(record)
    assert len(records) == 3
    assert records[0].message == 'event received'
    assert records[1].message.startswith('Unhandled ZeroDivisionError in')
    assert records[2].message == 'actor failed'

async def test_TextLogMiddleware(caplog: LogCaptureFixture) -> None:
    caplog.set_level(logging.DEBUG)
    await run_actor(noop, 'hi', walnats.middlewares.TextLogMiddleware())
    records = []
    for record in caplog.records:
        if record.name.startswith('walnats'):
            records.append(record)
    assert len(records) == 2
    assert re.match(r'event .+: received by .+', records[0].message)
    assert re.match(r'event .+: processed by .+', records[1].message)

async def test_TextLogMiddleware__on_failure(caplog: LogCaptureFixture) -> None:
    caplog.set_level(logging.DEBUG)
    await run_actor(explode, 'hi', walnats.middlewares.TextLogMiddleware())
    records = []
    for record in caplog.records:
        if record.name.startswith('walnats'):
            records.append(record)
    assert len(records) == 3
    assert re.match(r'event .+: received by .+', records[0].message)
    assert records[1].message.startswith('Unhandled ZeroDivisionError in')
    assert re.match(r'event .+: actor .+ failed', records[2].message)

async def test_ErrorThresholdMiddleware__single_failure() -> None:
    mw = MockMiddleware()
    await run_actor(explode, 'hi', walnats.middlewares.ErrorThresholdMiddleware(mw))
    assert mw.hist == ['on_start']

async def test_ErrorThresholdMiddleware__multiple_failures() -> None:
    mw = MockMiddleware()
    await run_actor(
        explode, ['hi'] * 40,
        walnats.middlewares.ErrorThresholdMiddleware(mw, total_failures=1000),
    )
    assert Counter(mw.hist) == Counter(dict(on_start=40, on_failure=19))

async def test_ErrorThresholdMiddleware__multiple_failures_many_actors() -> None:
    mw = MockMiddleware()
    emw = walnats.middlewares.ErrorThresholdMiddleware(mw, actor_failures=100)
    await asyncio.gather(
        run_actor(explode, ['hi'] * 10, emw),
        run_actor(explode, ['hi'] * 30, emw),
    )
    assert Counter(mw.hist) == Counter(dict(on_start=40, on_failure=19))

async def test_ErrorThresholdMiddleware__on_success() -> None:
    mw = MockMiddleware()
    await run_actor(noop, 'hi', walnats.middlewares.ErrorThresholdMiddleware(mw))
    assert mw.hist == ['on_start', 'on_success']

@pytest.mark.skipif(CI, reason='the test fails on CI, see PR#2')
async def test_FrequencyMiddleware() -> None:
    switch = False

    async def handler(msg: str) -> None:
        nonlocal switch
        switch = not switch
        if switch:
            1 / 0

    mw = MockMiddleware()
    await run_actor(
        handler, ['hi'] * 40,
        walnats.middlewares.FrequencyMiddleware(mw),
    )
    assert Counter(mw.hist) == Counter(dict(on_success=1, on_failure=1, on_start=1))

async def test_StatsdMiddleware(udp_server: UDPLogProtocol) -> None:
    from datadog.dogstatsd import DogStatsd

    switch = False
    received = []

    async def handler(msg: str) -> None:
        nonlocal switch
        switch = not switch
        received.append(msg)
        if switch:
            1 / 0

    client = DogStatsd(port=udp_server.port, disable_telemetry=True)
    await run_actor(
        handler, ['hi'] * 40,
        walnats.middlewares.StatsdMiddleware(client),
    )
    assert len(received) == 40
    client.flush()
    await asyncio.sleep(.1)
    # remove numbers from `duration` metric, so it can be aggregated
    hist = [h.split(':0.')[0] for h in udp_server.hist]
    expected = [
        (r'walnats\..+\..+\.started:1\|c', 40),
        (r'walnats\..+\..+\.failed:1\|c', 20),
        (r'walnats\..+\..+\.processed:1\|c', 20),
        (r'walnats\..+\..+\.duration', 20),
    ]
    fuzzy_match_counter(hist, expected)

async def test_PrometheusMiddleware() -> None:
    switch = False

    async def handler(msg: str) -> None:
        nonlocal switch
        switch = not switch
        if switch:
            1 / 0

    await run_actor(
        handler, ['hi'] * 40,
        walnats.middlewares.PrometheusMiddleware(),
    )

async def test_SentryMiddleware__smoke() -> None:
    await run_actor(explode, 'hi', walnats.middlewares.SentryMiddleware())

@pytest.mark.skipif(not SENTRY_DSN, reason='SENTRY_DSN env var is not provided')
async def test_SentryMiddleware__real_sentry() -> None:
    with sentry_sdk.init(SENTRY_DSN):
        await run_actor(explode, 'hi', walnats.middlewares.SentryMiddleware())
        sentry_sdk.flush()

async def test_ZipkinMiddleware() -> None:
    endpoint = aiozipkin.create_endpoint('test_service')
    transport = StubTransport()
    async with aiozipkin.create_custom(endpoint, transport) as tracer:

        # emit a trace for normal operation
        await run_actor(noop, 'hi', walnats.middlewares.ZipkinMiddleware(tracer))
        assert len(transport.records) == 1
        r = transport.records[-1]
        tags = r.asdict()['tags']
        assert set(tags) == {'event'}

        # include exception on failure
        await run_actor(explode, 'hi', walnats.middlewares.ZipkinMiddleware(tracer))
        assert len(transport.records) == 2
        r = transport.records[-1]
        tags = r.asdict()['tags']
        assert set(tags) == {'event', 'error'}
        assert tags['error'] == 'division by zero'

        # use provided trace_id if available
        await run_actor(
            noop, 'hi', walnats.middlewares.ZipkinMiddleware(tracer),
            trace_id='123',
        )
        assert len(transport.records) == 3
        r = transport.records[-1]
        tags = r.asdict()['tags']
        assert set(tags) == {'event'}
        assert r.asdict()['traceId'] == '123'

        for r in transport.records:
            assert r.asdict()['kind'] == aiozipkin.CONSUMER

async def test_CurrentContextMiddleware() -> None:
    mw = walnats.middlewares.CurrentContextMiddleware()
    received = []

    async def handler(msg: str) -> None:
        assert msg == mw.context.message
        received.append(msg)

    await run_actor(handler, [f'{i}' for i in range(40)], mw)
    assert len(received) == 40

async def test_OpenTelemetryTraceMiddleware__smoke() -> None:
    import opentelemetry.trace

    tracer = opentelemetry.trace.get_tracer('tests')
    await run_actor(
        explode, 'hi',
        walnats.middlewares.OpenTelemetryTraceMiddleware(tracer),
    )

    tracer = opentelemetry.trace.get_tracer('tests')
    await run_actor(
        noop, 'hi',
        walnats.middlewares.OpenTelemetryTraceMiddleware(tracer),
    )


from setuptools import find_packages, setup
import os

# Get more https://pypi.org/pypi?%3Aaction=list_classifiers
CLASSIFIERS = [
    "Development Status :: 5 - Production/Stable",
    # Indicate to who your project is directed
    "Environment :: Web Environment",
    "Framework :: Django",
    "Intended Audience :: Developers",
    # Indicate the used license (must match the "license")
    "License :: OSI Approved :: GNU General Public License (GPL)",
    "Operating System :: OS Independent",
    # Indicate supported versions, Python 2, Python 3 or both.
    "Programming Language :: Python",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
    "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
    "Topic :: Internet :: WWW/HTTP :: WSGI",
    "Topic :: Software Development",
    "Topic :: Software Development :: Libraries :: Application Frameworks",
    "Topic :: Software Development :: Libraries :: Python Modules",
]

EXCLUDE_FROM_PACKAGES = ["project","project.*"]

def read(fname):
    with open(os.path.join(os.path.dirname(__file__), fname)) as f:
        return f.read()

setup(
    name="django-core",
    version="0.1",
    description="A Django 'External API Integration' program example",
    long_description=read('README.rst'),
    classifiers=CLASSIFIERS,
    keywords="django external API integration example django program",
    author="Muhammad Ilyas",
    author_email="ilyas@gybcommerce.com",
    url="https://github.com/GYBCommerce/django-external-integration",
    license="GPL",
    platforms="OS Independent",
    install_requires=["Django==2.2.3"],
    packages=find_packages(exclude=EXCLUDE_FROM_PACKAGES),
    include_package_data=True,
    zip_safe=False,
    project_urls={
        'Documentation': 'https://docs.djangoproject.com/',
        'Source': 'https://github.com/GYBCommerce/django-external-integration',
        'Tracker': 'https://github.com/GYBCommerce/django-external-integration/issues',
    },
)