import os
import glob
import errno
import signal
import json
import sys
import inspect
import shutil
import subprocess
import time
import re
import multiprocessing as mp
import pprint

from multiprocessing import Process
from concurrent.futures import ThreadPoolExecutor
from typing import Text, List, Dict, Union
from importlib import import_module
from datetime import date, datetime
from collections import OrderedDict

import grpc
import jsbeautifier

from google.protobuf.message import Message
from appyratus.utils.string_utils import StringUtils
from appyratus.utils.func_utils import FuncUtils
from appyratus.utils.dict_utils import DictUtils, DictObject
from appyratus.utils.time_utils import TimeUtils
from appyratus.utils.template_utils import TemplateEnvironment

from ravel.util import (
    is_resource,
    is_batch,
    get_class_name,
    is_port_in_use,
)
from ravel.util.loggers import console
from ravel.util.json_schema import JsonSchemaGenerator
from ravel.app.base import Application
from ravel.schema import Schema, fields

from .grpc_method import GrpcMethod
from .grpc_client import GrpcClient
from .util import get_stripped_schema_name
from .proto import MessageGenerator
from .constants import (
    DEFAULT_HOST,
    DEFAULT_PORT,
    DEFAULT_GRACE,
    DEFAULT_IS_SECURE,
    REGISTRY_PROTO_FILE,
    PB2_MODULE_PATH_FSTR,
    PB2_GRPC_MODULE_PATH_FSTR,
    PROTOC_COMMAND_FSTR,
    PROTOC_WEB_GENERATE_CLIENT_COMMAND,
    PROTOC_WEB_GENERATE_MESSAGE_CLASSES_COMMAND,
)

class GrpcOptionsSchema(Schema):
    """
    Manifest gRPC options schema
    """
    client_host = fields.String(required=True, default=DEFAULT_HOST)
    server_host = fields.String(required=True, default=DEFAULT_HOST)
    secure_channel = fields.Bool(required=True, default=DEFAULT_IS_SECURE)
    port = fields.String(required=True, default=DEFAULT_PORT)
    grace = fields.Float(required=True, default=DEFAULT_GRACE)

class GrpcService(Application):
    """
    Grpc server.

    All configuration options and data structures pertaining to gRPC itself are
    kept in self.grpc. For the most part, this object is constructed during
    bootstrap.

    It is possible to generate a gRPC client for the service via the "client"
    property. The service must be bootstrapped before accessing the client.

    Note that gRPC manages its server as a thread pool. When we call
    GrpcService.start, we are really just starting the threads in the pool and
    then putting the main thread in a sleep lock.
    """

    def __init__(self, middleware=None, *args, **kwargs):
        super().__init__(middleware=middleware, *args, **kwargs)
        self.grpc = DictObject()
        self.grpc.options = DictObject()
        self.grpc.worker_process_count = mp.cpu_count()
        self.grpc.worker_thread_pool_size = 1
        self.grpc.pb2 = None
        self.grpc.pb2_grpc = None
        self.grpc.server = None
        self.grpc.servicer = None
        self._client = None

    @property
    def client(self) -> GrpcClient:
        """
        Return a memoized GrpcClient instance. However, if the service isn't
        bootstrapped, None will be returned, as the client derives itself
        from a bootstrapped service.
        """
        if not self.is_bootstrapped:
            return None
        elif self._client is None:
            self._client = GrpcClient(self)
        return self._client

    @property
    def action_type(self):
        return GrpcMethod

    def on_bootstrap(self, options: Dict = None, build=False):
        """
        Compute gRPC configuration variables, build the proto file and grpcio
        "pb2" Python modules, and compute other metadata needed to route
        requests to RPC API methods.
        """
        self._on_bootstrap_configure_grpc(options)
        self._on_bootstrap_build_and_import_grpc_modules(build)
        self._on_bootstrap_aggregate_response_message_types()

    def build(self, manifest=None, options: Dict = None):
        self.bootstrap(manifest=manifest, build=True, options=options)

    def build_js(self, manifest: Text, dest: Text):
        """
        Generate JavaScript files containing the porotobuf message types and
        client stub.
        """
        # TODO: move these methods into a grpc_web module
        self.bootstrap(manifest=manifest, build=False)

        # ensure template build directory exists
        dest = os.path.join(dest, 'grpc')
        try:
            os.makedirs(dest)
        except OSError as exc:
            if exc.errno != errno.EEXIST:
                raise

        self._compile_grpc_web_message_classes(dest)
        self._compile_grpc_web_client_stub(dest)
        self._render_grpc_web_client_files(dest)

    def _render_grpc_web_client_files(self, dest):
        dest = os.path.realpath(dest)

        # generate JSON schemas for all ravel schemas
        # referenced in this app
        def unnest(schema, unnested):
            for child_schema in schema.children:
                child_schema_name = get_stripped_schema_name(child_schema)
                if child_schema_name in unnested:
                    console.warning(
                        message=(
                            f'gRPC web client build process '
                            f'ignoring schema with duplicate name'
                        ),
                        data={
                            'schema': child_schema_name,
                            'parent_schema': get_stripped_schema_name(schema),
                        }
                    )
                unnested[child_schema_name] = schema
                unnest(child_schema, unnested)

        nested_schemas = OrderedDict()
        for resource_type in self.res.values():
            unnest(resource_type.ravel.schema, nested_schemas)

        action_schemas = OrderedDict()
        for action in self.actions.values():
            for schema in action.schemas.values():
                action_schemas[get_stripped_schema_name(schema)] = schema

        # initialize templating engine
        template_dir = os.path.join(os.path.dirname(__file__), 'templates')
        jinja = TemplateEnvironment(template_dir)
        template_context = {
            'list': list,
            'dict': dict,
            'str': str,
            'int': int,
            'set': set,
            'len': len,
            'app': self,
            'inspect': inspect,
            'nested_schemas': nested_schemas,
            'action_schemas': action_schemas,
            'json_schema_generator': JsonSchemaGenerator(),
            'class_name': get_class_name,
        }

        console.info(
            message=f'jinja2 template directory',
            data={'path': template_dir}
        )

        # render templates
        for js_fname in glob.glob(template_dir + '/*.js'):
            js_fname = os.path.basename(js_fname)
            with open(os.path.join(dest, js_fname), 'w') as js_file:
                # render template from file
                template = jinja.from_filename(js_fname)
                js_source = template.render(template_context)
                # lint the generated JS code
                formated_js_source = jsbeautifier.beautify(js_source)
                # write to dest directory
                console.info(f'rendering {dest}/{os.path.basename(js_fname)}')
                js_file.write(formated_js_source)

    def on_decorate(self, action):
        pass

    def on_request(self, action, request, *args, **kwargs):
        """
        Take the attributes on the incoming protobuf Message object and
        map them to the args and kwargs expected by the action target.
        """
        console.debug(
            message=f'RPC method {action.name} requested',
        )

        # get field data to process into args and kwargs
        def deserialize_value(field, value):
            if isinstance(field, fields.Dict):
                if not value:
                    value = None if field.nullable else {}
                else:
                    value = self.json.decode(value)
            return value

        all_arguments = {}
        for field in action.schemas.request.fields.values():
            value = getattr(request, field.name, None)
            all_arguments[field.name] = deserialize_value(field, value)

        # add a dummy request object so that partition_arguments can
        # run below; then remove it from the resulting args tuple.
        all_arguments['request'] = None

        # process field_data into args and kwargs
        args, kwargs = FuncUtils.partition_arguments(
            action.signature, all_arguments
        )

        args = args[1:]

        return (args, kwargs)

    def on_response(self, action, result, *raw_args, **raw_kwargs):
        """
        Map the return dict from the action to the expected outgoing protobuf
        response Message object.
        """
        response_type = self.grpc.response_types[action.name]
        response = response_type()
        response_data = None

        def validate(data):
            data, errors = schema.process(data)
            if errors:
                console.error(
                    message=f'response validation errors',
                    data={
                        'data': pprint.pformat(data),
                        'errors': errors,
                    }
                )
            return data

        if result:
            schema = action.schemas.response
            dumped_result = self._dump_result_obj(result)
            if isinstance(dumped_result, list):
                response_data = [validate(x) for x in dumped_result]
            else:
                response_data = validate(dumped_result)

        if response_data:
            if isinstance(response_data, list):
                response = iter(
                    self.build_response_message(response_type(), data)
                    for data in response_data
                )
            else:
                response = self.build_response_message(response, response_data)

        return response

    @staticmethod
    def on_start_worker(app):
        """
        This method runs as the "initializer" for each thread in the grpc
        server's thread pool. Override in subclass.
        """

    def on_start(self):
        """
        Start the RPC client or server.
        """
        # try to ensure the desired port is available
        if is_port_in_use(self.grpc.options.server_address):
            console.critical(
                f'gRPC service address {self.grpc.options.server_address} '
                f'in use. trying to kill stale service processes.'
            )
            server_port = self.grpc.options.port
            try:
                kill_cmd = f'lsof -ti tcp:{server_port} | xargs kill'
                console.info(
                    message=f'running command to free gRPC service port',
                    data={'port': server_port, 'command': kill_cmd}
                )
                subprocess.getoutput(kill_cmd)
            except Exception:
                console.exception(
                    f'error running command to free gRPC service port'
                )
                exit(-1)
            if is_port_in_use(self.grpc.options.server_address):
                console.critical('could not start gRPC service')
                exit(-1)

        console.info(
            message='strating gRPC server. Press ctrl+c to stop.',
            data={
                'address': self.grpc.options.server_address,
                'methods': list(self.actions.keys()),
                'worker_process_count': self.grpc.worker_process_count,
                'worker_thread_pool_size': self.grpc.worker_thread_pool_size,
            }
        )
        # start the server process/es
        if self.grpc.worker_process_count == 1:
            # serve in main forground process
            self._init_server_and_run()
        else:
            # serve in forked subprocesses
            self._spawn_worker_processes()

    def _spawn_worker_processes(self):
        processes = []

        # entrypoint or "target" function for server processes
        def entrypoint(service):
            service.bootstrap(force=True)
            service._init_server_and_run()

        # create and run the server processes
        for _ in range(self.grpc.worker_process_count):
            process = Process(target=entrypoint, args=(self, ))
            processes.append(process)
            process.start()

        self._setup_os_signal_handling_when_multiprocessing()

        for process in processes:
            process.join(timeout=5)

    def _setup_os_signal_handling_when_multiprocessing(self):
        def handler(signum, frame):
            console.debug(
                'ignoring SIGINT in root process. joining '
                'all server subprocesses.'
            )
        # set the signal handler
        signal.signal(signal.SIGINT, handler)

    def _init_server_and_run(self):
        # the grpc server runs in a thread pool
        self.grpc.executor = ThreadPoolExecutor(
            max_workers=self.grpc.worker_thread_pool_size,
            initializer=self.on_start_worker,
            initargs=(self, )
        )

        for i in range(self.grpc.worker_thread_pool_size):
            self.grpc.executor.submit(lambda: None)

        # build grpc server
        self.grpc.server = grpc.server(self.grpc.executor)
        self.grpc.server.add_insecure_port(self.grpc.options.server_address)

        # build grpcio "servicer" class
        self.grpc.servicer = self._grpc_servicer_factory()
        self.grpc.servicer.add_to_grpc_server(
            self.grpc.servicer, self.grpc.server
        )

        self.grpc.server.start()

        # sleep-lock the main thread to keep the server running,
        # as the server runs as a daemon.
        try:
            while True:
                time.sleep(9999)
        except KeyboardInterrupt:
            # stop server on ctrl+c
            if self.grpc.server is not None:
                console.info(f'stopping gRPC server (PID: {os.getpid()})')
                self.grpc.server.stop(grace=self.grpc.options.grace)

    def _on_bootstrap_configure_grpc(self, options: Dict):
        """
        Compute the gRPC options dict and other values needed to run this
        application as a gRPC service.
        """
        grpc = self.grpc

        # get the python package containing this Application
        pkg_path = self.manifest.package
        pkg = import_module(self.manifest.package)

        # compute some file and module paths
        grpc.pkg_dir = os.path.dirname(pkg.__file__)
        grpc.build_dir = os.path.join(grpc.pkg_dir, 'grpc')
        grpc.proto_file = os.path.join(grpc.build_dir, REGISTRY_PROTO_FILE)

        # compute grpc options dict from options kwarg and manifest data
        kwarg_options = options or {}
        manifest_options = self.manifest.data.get('grpc', {})
        computed_options = DictUtils.merge(kwarg_options, manifest_options)

        # generate default values into gRPC options data
        schema = GrpcOptionsSchema()
        options, errors = schema.process(computed_options)
        if errors:
            raise Exception(f'invalid gRPC options: {errors}')

        grpc.options = DictObject(options)
        grpc.options.port = port = options['port']
        grpc.options.server_host = server_host = options['server_host']
        grpc.options.client_host = client_host = options['client_host']
        grpc.options.server_address = (f'{server_host}:{port}')
        grpc.options.client_address = (f'{client_host}:{port}')
        grpc.pb2_mod_path = PB2_MODULE_PATH_FSTR.format(pkg_path)
        grpc.pb2_grpc_mod_path = PB2_GRPC_MODULE_PATH_FSTR.format(pkg_path)

        # add build directory to PYTHONPATH; otherwise, gRPC won't know where
        # the pb2 modules are when it tries to import them.
        sys.path.append(grpc.build_dir)

    def _on_bootstrap_build_and_import_grpc_modules(self, build: bool):
        """
        Call an external grpcio command to generate the gRPC python modules
        necessary for running this service. Then we import them, because we
        need to derive client and servicer subclasses and procedures from the
        autogenerated base components contained therein.
        """
        grpc = self.grpc
        pkg_path = self.manifest.package

        # try to import the pb2 module just to see if it exists
        try:
            import_module(grpc.pb2_mod_path, pkg_path)
            pb2_module_dne = False    # dne: does not exist
        except Exception:
            console.warning(f'{grpc.pb2_mod_path} does not exist. rebuilding')
            pb2_module_dne = True

        # build the pb2 and pb2_grpc modules
        if build or pb2_module_dne:
            self._build_pb2_modules()
            time.sleep(0.25)

        # now import the dynamically-generated pb2 modules
        grpc.pb2 = import_module(grpc.pb2_mod_path, pkg_path)
        grpc.pb2_grpc = import_module(grpc.pb2_grpc_mod_path, pkg_path)

    def _on_bootstrap_aggregate_response_message_types(self):
        """
        Build a lookup table of protobuf response Message types for use when
        routing requests to their downstream actions.
        """
        grpc = self.grpc
        grpc.response_types = {}
        for action in self.actions.values():
            schema_type_name = get_class_name(action.schemas.response)
            message_type_name = get_stripped_schema_name(
                StringUtils.camel(schema_type_name)
            )
            response_message_type = getattr(self.grpc.pb2, message_type_name)
            grpc.response_types[action.name] = response_message_type

    def _grpc_servicer_factory(self):
        """
        Import the abstract base Servicer class from the autogenerated
        pb2_grpc module and derive a new subclass that inherits this
        Application's action objects as its interface implementation.
        """
        servicer_type_name = 'GrpcApplicationServicer'
        abstract_type = None

        # get a reference to the grpc abstract base Servicer  class
        for k, v in inspect.getmembers(self.grpc.pb2_grpc):
            if k == servicer_type_name:
                abstract_type = v
                break
        if abstract_type is None:
            raise Exception('could not find grpc Servicer class')

        # create dynamic Servicer subclass
        servicer_type = type(
            servicer_type_name, (abstract_type, ), self.actions.to_dict()
        )
        servicer = servicer_type()

        # register the Servicer with the server
        servicer.add_to_grpc_server = (
            self.grpc.pb2_grpc.add_GrpcApplicationServicer_to_server
        )

        return servicer

    def _build_pb2_modules(self):
        """
        Create the build dir inside the host package, generating the proto file
        and grpcio modules into it.
        """
        # recreate the build directory
        if os.path.isdir(self.grpc.build_dir):
            shutil.rmtree(self.grpc.build_dir)

        os.makedirs(os.path.join(self.grpc.build_dir), exist_ok=True)

        # touch the __init__.py file
        with open(os.path.join(self.grpc.build_dir, '__init__.py'), 'a'):
            pass

        # upsert the .ravel file in the build dir with a directive to ignore
        # this directory during project component discovery.
        dot_file_name = os.path.join(self.grpc.build_dir, '.ravel')
        if not os.path.isfile(dot_file_name):
            with open(dot_file_name, 'w') as dot_file:
                json.dump({'scanner': {'ignore': True}}, dot_file)
        else:
            with open(dot_file_name, 'rw') as dot_file:
                dot_data = json.load(dot_file) or {}
                dot_data.setdefault('scanner', {})['ignore'] = True
                json.dump(dot_data, dot_file)

        # generate the .proto file and generate grpc python modules from it
        self._generate_proto_file()
        self._compile_pb2_modules()

    def _generate_proto_file(self):
        """
        Iterate over function actions, using request and response schemas to
        generate protobuf message and service types.
        """
        # all lines in proto source code accumulated into "lines"
        lines = ['syntax = "proto3";']

        # service interface function delaration string accumulator
        decls = []

        # generate proto messages for each Resource class in this app
        msg_gen = MessageGenerator()
        for name, resource_type in self.res.items():
            lines.append(msg_gen.emit_resource_message(resource_type) + '\n')

        # generate request and response schemas along with service
        # interface function declarations, derived them application actions
        for action in self.actions.values():
            decls.append(action.generate_protobuf_function_declaration())
            if action.schemas.request:
                lines.append(action.generate_request_message_type())
            if action.schemas.response:
                lines.append(action.generate_response_message_type())

        # assemble service interface definition block
        lines.append('service GrpcApplication {')
        for decl in decls:
            lines.append('  ' + decl)
        lines.append('}\n')

        # write final protobuf source code to file
        console.info(
            message='generating gRPC proto file',
            data={'destination': self.grpc.proto_file}
        )
        with open(self.grpc.proto_file, 'w') as fout:
            fout.write('\n'.join(lines))

    def _compile_pb2_modules(self):
        """
        Compile the grpc .proto file, generating pb2 and pb2_grpc modules in
        the build directory. These modules contain abstract base classes
        required by the grpc server and client.
        """
        # build the shell command to run....
        protoc_command = PROTOC_COMMAND_FSTR.format(
            include_dir=os.path.realpath(
                os.path.dirname(self.grpc.proto_file)
            ) or '.',
            build_dir=self.grpc.build_dir,
            proto_file=os.path.basename(self.grpc.proto_file),
        ).strip()

        console.info(
            message='generating gRPC pb2 modules',
            data={'command': protoc_command.split('\n')}
        )

        err_msg = subprocess.getoutput(
            re.sub(r'\s+', ' ', protoc_command)
        )
        if err_msg:
            exit(err_msg)

    def _compile_grpc_web_message_classes(self, dest: Text):
        """
        Compile the grpc .proto file, generating pb2 and pb2_grpc modules in
        the build directory. These modules contain abstract base classes
        required by the grpc server and client.
        """
        # build the shell command to run....
        protoc_command = PROTOC_WEB_GENERATE_MESSAGE_CLASSES_COMMAND.format(
            include_dir=os.path.realpath(
                os.path.dirname(self.grpc.proto_file)
            ) or '.',
            build_dir=dest,
            proto_file=os.path.basename(self.grpc.proto_file),
        ).strip()

        console.info(
            message='generating gRPC-web message classes',
            data={'command': protoc_command.split('\n')}
        )
        err_msg = subprocess.getoutput(
            re.sub(r'\s+', ' ', protoc_command)
        )
        if err_msg:
            exit(err_msg)

    def _compile_grpc_web_client_stub(self, dest: Text):
        """
        Compile the grpc .proto file, generating pb2 and pb2_grpc modules in
        the build directory. These modules contain abstract base classes
        required by the grpc server and client.
        """
        # build the shell command to run....
        protoc_command = PROTOC_WEB_GENERATE_CLIENT_COMMAND.format(
            include_dir=os.path.realpath(
                os.path.dirname(self.grpc.proto_file)
            ) or '.',
            build_dir=dest,
            proto_file=os.path.basename(self.grpc.proto_file),
        ).strip()

        console.info(
            message='generating gRPC-web client stub',
            data={'command': protoc_command.split('\n')}
        )
        err_msg = subprocess.getoutput(
            re.sub(r'\s+', ' ', protoc_command)
        )
        if err_msg:
            exit(err_msg)

    def build_response_message(
        self,
        message: Message,
        source: Dict
    ) -> Message:
        """
        Recursively bind each value in a source dict to the corresponding
        attribute in a grpc Message object, resulting in the prepared response
        message, ready to be sent back to the client.
        """
        if source is None:
            return None

        for k, v in source.items():
            if v is None:
                # protobufs don't understand null values
                continue

            # TODO: move conversion logic into adapters
            if isinstance(v, (date, datetime)):
                ts = TimeUtils.to_timestamp(v)
                setattr(message, k, ts)
            elif isinstance(getattr(message, k), Message):
                sub_message = getattr(message, k)
                assert isinstance(v, dict)
                self.build_response_message(sub_message, v)
            elif isinstance(v, dict):
                json_str = self.json.encode(v)
                setattr(message, k, json_str)
            elif isinstance(v, (list, tuple, set)):
                list_field = getattr(message, k)
                sub_message_type_name = (
                    '{}Schema'.format(k.title().replace('_', ''))
                )
                sub_message_type = getattr(
                    message, sub_message_type_name, None
                )
                if sub_message_type:
                    list_field.extend(
                        self.build_response_message(sub_message_type(), v_i)
                        for v_i in v
                    )
                else:
                    v_prime = [
                        x if not isinstance(x, dict) else self.json.encode(x)
                        for x in v
                    ]
                    list_field.extend(v_prime)
            else:
                setattr(message, k, v)

        return message

    def _dump_result_obj(self, obj) -> Union[Dict, List]:
        """
        Currently, GrpcService is intended to be used for internal
        service-to-service comm. This is why we "dump" Resources here by simply
        returning their internal state dicts.
        """
        if is_resource(obj):
            return obj.internal.state
        elif is_batch(obj):
            return [x.internal.state for x in obj]
        elif isinstance(obj, (list, set, tuple)):
            return [self._dump_result_obj(x) for x in obj]
        elif isinstance(obj, dict):
            return {k: self._dump_result_obj(v) for k, v in obj.items()}
        else:
            return obj


import numpy as np
import trimesh
import open3d

def sample_points_from_tet_mesh(mesh, k):

    """
    Sample points from the tetrahedral mesh by executing the following procedure:
    1) Sampling k points from each tetrahedron by computing the weighted average location of the 4 vertices with random weights.

    """

    def generate_random_weights(k):
        # Generate random weights for k points
        weights = np.random.rand(k, 4)
        # Normalize the weights so that they sum up to 1 for each point
        normalized_weights = weights / np.sum(weights, axis=1, keepdims=True)
        return normalized_weights

    def compute_weighted_average(vertices, weights):
        # Compute the weighted average for each set of weights and vertices
        return np.einsum('ijk,ij->ik', vertices, weights)

    num_tetrahedra = mesh.shape[0]
    vertices = mesh.reshape(num_tetrahedra, 4, -1)

    points = []

    for _ in range(k):
        # Generate random weights for all tetrahedra and points at once
        weights_list = generate_random_weights(k=1)

        # Compute the weighted average location for all points and vertices
        sampled_points = compute_weighted_average(vertices, weights_list)

        points.append(sampled_points)

    sampled_points = np.concatenate((points), axis=0)

    return sampled_points

def create_tet_mesh(mesh_dir, intput_tri_mesh_name, output_tet_mesh_name=None, mesh_extension='.stl',
                    coarsen=True, verbose=False, fTetWild_dir='/home/baothach/fTetWild/build'):

    if output_tet_mesh_name is None:
        output_tet_mesh_name = intput_tri_mesh_name

    # surface mesh (.stl, .obj, etc.) to volumetric mesh (.mesh)
    import os
    os.chdir(fTetWild_dir)
    mesh_path = os.path.join(mesh_dir, intput_tri_mesh_name + mesh_extension)
    save_fTetwild_mesh_path = os.path.join(mesh_dir, output_tet_mesh_name + '.mesh')

    if coarsen:
        os.system("./FloatTetwild_bin -o " + save_fTetwild_mesh_path + " -i " + mesh_path + " --coarsen >/dev/null")
    else:
        os.system("./FloatTetwild_bin -o " + save_fTetwild_mesh_path + " -i " + mesh_path + " >/dev/null")

    # .mesh to .tet:
    mesh_file = open(os.path.join(mesh_dir, output_tet_mesh_name + '.mesh'), "r")
    tet_output = open(
        os.path.join(mesh_dir, output_tet_mesh_name + '.tet'), "w")

    # Parse .mesh file
    mesh_lines = list(mesh_file)
    mesh_lines = [line.strip('\n') for line in mesh_lines]
    vertices_start = mesh_lines.index('Vertices')
    num_vertices = mesh_lines[vertices_start + 1]

    vertices = mesh_lines[vertices_start + 2:vertices_start + 2
                        + int(num_vertices)]

    tetrahedra_start = mesh_lines.index('Tetrahedra')
    num_tetrahedra = mesh_lines[tetrahedra_start + 1]
    tetrahedra = mesh_lines[tetrahedra_start + 2:tetrahedra_start + 2
                            + int(num_tetrahedra)]

    if verbose:
        print("# Vertices, # Tetrahedra:", num_vertices, num_tetrahedra)

    # Write to tet output
    tet_output.write("# Tetrahedral mesh generated using\n\n")
    tet_output.write("# " + num_vertices + " vertices\n")
    for v in vertices:
        tet_output.write("v " + v + "\n")
    tet_output.write("\n")
    tet_output.write("# " + num_tetrahedra + " tetrahedra\n")
    for t in tetrahedra:
        line = t.split(' 0')[0]
        line = line.split(" ")
        line = [str(int(k) - 1) for k in line]
        l_text = ' '.join(line)
        tet_output.write("t " + l_text + "\n")

def simplify_mesh_pymeshlab(mesh, target_num_vertices):

    import pymeshlab as ml

    if not isinstance(mesh, trimesh.Trimesh):
        raise ValueError("Invalid mesh type. Must be a Trimesh object")

	# https://stackoverflow.com/questions/65419221/how-to-use-pymeshlab-to-reduce-vertex-number-to-a-certain-number/65424578#65424578
    numFaces = 100 + 2*target_num_vertices

    ms = ml.MeshSet()
    pymeshlab_mesh = ml.Mesh(mesh.vertices, mesh.faces)
    ms.add_mesh(pymeshlab_mesh)

    while (ms.current_mesh().vertex_number() > target_num_vertices):
        ms.apply_filter('simplification_quadric_edge_collapse_decimation', targetfacenum=numFaces, preservenormal=True)
        numFaces = numFaces - (ms.current_mesh().vertex_number() - target_num_vertices)

    return trimesh.Trimesh(vertices=ms.current_mesh().vertex_matrix(), faces=ms.current_mesh().face_matrix())

def trimesh_to_open3d_mesh(trimesh_mesh):
    # Access vertices and faces of the trimesh mesh
    vertices = np.array(trimesh_mesh.vertices)
    faces = np.array(trimesh_mesh.faces)

    # Create an open3d TriangleMesh from the vertices and faces
    open3d_mesh = open3d.geometry.TriangleMesh()
    open3d_mesh.vertices = open3d.utility.Vector3dVector(vertices)
    open3d_mesh.triangles = open3d.utility.Vector3iVector(faces)

    return open3d_mesh

def open3d_to_trimesh_mesh(o3d_mesh):
    vertices = np.asarray(o3d_mesh.vertices)
    triangles = np.asarray(o3d_mesh.triangles)
    trimesh_mesh = trimesh.Trimesh(vertices=vertices, faces=triangles)
    return trimesh_mesh

def visualize_voxel(voxel_grid):
    import matplotlib.pyplot as plt
    # Create a figure and a 3D axis
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')

    # Get the coordinates of occupied voxels
    x, y, z = np.where(voxel_grid)

    # Plot the occupied voxels as points
    ax.scatter(x, y, z, c='b', marker='o', s=10)

    # Set labels for the axes
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')

    # Show the plot
    plt.show()

def voxelize_point_cloud(point_cloud, grid_size=[32]*3, vis=False):
    # max_point = 2*np.max(np.abs(point_cloud))
    # grid_size = int(np.ceil(2 * max_point / voxel_size)) + 1  # Calculate grid size and add 1

    # # Initialize voxel grid
    # voxel_grid = np.zeros((grid_size, grid_size, grid_size), dtype=bool)

    # # Iterate over points and fill the voxel grid
    # for point in point_cloud:
    #     voxel_coordinates = np.floor((point + max_point) / voxel_size).astype(int)
    #     if (0 <= voxel_coordinates).all() and (voxel_coordinates < grid_size).all():
    #         voxel_grid[voxel_coordinates[0], voxel_coordinates[1], voxel_coordinates[2]] = True

    from pyntcloud import PyntCloud

    n_x, n_y, n_z = grid_size[0], grid_size[1], grid_size[2]

    pcd = open3d.geometry.PointCloud()
    pcd.points = open3d.utility.Vector3dVector(point_cloud)
    cloud = PyntCloud.from_instance('OPEN3D', pcd)
    voxelgrid_id = cloud.add_structure("voxelgrid", n_x=n_x, n_y=n_x, n_z=n_y)
    # voxelgrid_id = cloud.add_structure("voxelgrid", size_x=grid_size[0], size_y=grid_size[1], size_z=grid_size[2])
    voxelgrid = cloud.structures[voxelgrid_id]

    voxel_array = np.zeros((n_x, n_y, n_z))

    for (x,y,z) in zip(voxelgrid.voxel_x, voxelgrid.voxel_y, voxelgrid.voxel_z):
        voxel_array[x,y,z] = 1

    if vis:
        visualize_voxel(voxel_array)

    return voxel_array

def point_cloud_to_mesh_mcubes(point_cloud, grid_size, use_mcubes_smooth=False, vis_voxel=False, vis_mesh=False):
    import mcubes

    voxel_grid = voxelize_point_cloud(point_cloud, grid_size, vis=vis_voxel)

    if use_mcubes_smooth:
        voxel_grid = mcubes.smooth(voxel_grid)

    # Extract the 0-isosurface
    vertices, triangles = mcubes.marching_cubes(voxel_grid, 0)
    mesh = trimesh.Trimesh(vertices=vertices, faces=triangles)

    if vis_mesh:
        mesh.show()

    return mesh

def is_occupied(x, y, z, pre_voxelized):
    '''
    x,y,z is bottom left.
    '''

    # Check if all are on.
    return (pre_voxelized[x,y,z] == 1.0 and
            pre_voxelized[x,y,z+1] == 1.0 and
            pre_voxelized[x,y+1,z] == 1.0 and
            pre_voxelized[x,y+1,z+1] == 1.0 and
            pre_voxelized[x+1,y,z] == 1.0 and
            pre_voxelized[x+1,y,z+1] == 1.0 and
            pre_voxelized[x+1,y+1,z] == 1.0 and
            pre_voxelized[x+1,y+1,z+1] == 1.0)

def is_active(x, y, z, pre_voxelized):
    '''
    Check if given x,y,z in given voxel grid is active.
    '''
    voxel_occupancy = pre_voxelized[x,y,z]

    base = pre_voxelized[x,y,z]
    return (pre_voxelized[x,y,z+1] != base or
            pre_voxelized[x,y+1,z] != base or
            pre_voxelized[x,y+1,z+1] != base or
            pre_voxelized[x+1,y,z] != base or
            pre_voxelized[x+1,y,z+1] != base or
            pre_voxelized[x+1,y+1,z] != base or
            pre_voxelized[x+1,y+1,z+1] != base)

def get_grid_points(active_voxels, current_voxel_resolution, bound):
    grid_pts = set()
    voxel_size = (2*bound) / float(current_voxel_resolution)
    for x,y,z in active_voxels:
        x_ = -bound + (((2*bound) / float(current_voxel_resolution)) * x)
        y_ = -bound + (((2*bound) / float(current_voxel_resolution)) * y)
        z_ = -bound + (((2*bound) / float(current_voxel_resolution)) * z)
        grid_pts.add((x_,y_,z_))
        grid_pts.add((x_,y_,z_+voxel_size))
        grid_pts.add((x_,y_+voxel_size,z_))
        grid_pts.add((x_,y_+voxel_size,z_+voxel_size))
        grid_pts.add((x_+voxel_size,y_,z_))
        grid_pts.add((x_+voxel_size,y_,z_+voxel_size))
        grid_pts.add((x_+voxel_size,y_+voxel_size,z_))
        grid_pts.add((x_+voxel_size,y_+voxel_size,z_+voxel_size))
    return np.array(list(grid_pts))

def mise_voxel(point_cloud, initial_voxel_resolution, final_voxel_resolution, voxel_size, centroid_diff=None):
    import mcubes
    '''
    get_sdf: map from query points to SDF (assume everything else already embedded in func (i.e., point cloud/embedding)).
    bound: sample within [-bound, bound] in x,y,z.
    initial/final_voxel_resolution: powers of two representing voxel resolution to evaluate at.
    voxel_size: size of each voxel (in final res) determined by view.
    centroid_diff: offset if needed.
    '''

    centered_pc = point_cloud - np.mean(point_cloud, axis=0)
    bounds = trimesh.PointCloud(centered_pc).bounds
    bound = max(bounds[1])

    # Active voxels: voxels we want to evaluate grid points of.
    active_voxels = []
    # Full voxelization.
    voxelized = np.zeros((final_voxel_resolution,final_voxel_resolution,final_voxel_resolution), dtype=np.float32)
    # Intermediate voxelization. This represents the grid points for the voxels (so is resolution + 1 in each dim).
    partial_voxelized = None

    # Init active voxels to all voxels in the initial resolution.
    for x in range(initial_voxel_resolution):
        for y in range(initial_voxel_resolution):
            for z in range(initial_voxel_resolution):
                active_voxels.append([x, y, z])
    active_voxels = np.array(active_voxels)

    # Start main loop that ups resolution.
    current_voxel_resolution = initial_voxel_resolution
    while current_voxel_resolution <= final_voxel_resolution:
        # print(current_voxel_resolution)

        # Setup voxelizations at this dimension.
        partial_voxelized = np.zeros((current_voxel_resolution + 1,current_voxel_resolution + 1,current_voxel_resolution + 1), dtype=np.float32)

        # For all points sample SDF given the point cloud.
        for pt_ in centered_pc:
            # Convert points into grid voxels and set.
            x_ = int(round(((pt_[0] + bound)/(2 * bound)) * float(current_voxel_resolution)))
            y_ = int(round(((pt_[1] + bound)/(2 * bound)) * float(current_voxel_resolution)))
            z_ = int(round(((pt_[2] + bound)/(2 * bound)) * float(current_voxel_resolution)))
            partial_voxelized[x_,y_,z_] = 1.0

        # Determine filled and active voxels.
        new_active_voxels = []
        for x,y,z in active_voxels:
            if is_occupied(x, y, z, partial_voxelized):
                # Set all associated voxels on in full voxelization.
                voxels_per_voxel = final_voxel_resolution // current_voxel_resolution

                # Set all corresponding voxels in the full resolution to on.
                for x_ in range(voxels_per_voxel*x, voxels_per_voxel*x + voxels_per_voxel):
                    for y_ in range(voxels_per_voxel*y, voxels_per_voxel*y + voxels_per_voxel):
                        for z_ in range(voxels_per_voxel*z, voxels_per_voxel*z + voxels_per_voxel):
                            voxelized[x_, y_, z_] = 1.0
            elif is_active(x, y, z, partial_voxelized):
                # If final resolution, just set it as active.
                if current_voxel_resolution == final_voxel_resolution:
                    voxelized[x,y,z] = 1.0
                    continue

                # Up voxel position to match doubling of voxel resolution.
                x_base = 2*x
                y_base = 2*y
                z_base = 2*z

                # Add new voxels for higher resolution. Each voxel gets split into 8 new.
                new_active_voxels.append([x_base, y_base, z_base])
                new_active_voxels.append([x_base, y_base, z_base+1])
                new_active_voxels.append([x_base, y_base+1, z_base])
                new_active_voxels.append([x_base, y_base+1, z_base+1])
                new_active_voxels.append([x_base+1, y_base, z_base])
                new_active_voxels.append([x_base+1, y_base, z_base+1])
                new_active_voxels.append([x_base+1, y_base+1, z_base])
                new_active_voxels.append([x_base+1, y_base+1, z_base+1])
        active_voxels = np.array(new_active_voxels)
        current_voxel_resolution = current_voxel_resolution * 2

    # print("Done with extraction.")

    # Padding to prevent holes if go up to edge.
    voxels = voxelized
    voxelized = np.pad(voxelized, ((1,1),(1,1),(1,1)), mode='constant')

    # Mesh w/ mcubes.
    voxelized = mcubes.smooth(voxelized)
    vertices, triangles = mcubes.marching_cubes(voxelized, 0)
    vertices = vertices * voxel_size

    # Center mesh.
    vertices[:,0] -= voxel_size * (((final_voxel_resolution) / 2) + 1)
    vertices[:,1] -= voxel_size * (((final_voxel_resolution) / 2) + 1)
    vertices[:,2] -= voxel_size * (((final_voxel_resolution) / 2) + 1)

    # vertices[:,0] -= centroid_diff[0]
    # vertices[:,1] -= centroid_diff[1]
    # vertices[:,2] -= centroid_diff[2]

    mesh = trimesh.Trimesh(vertices=vertices, faces=triangles)

    return mesh # convert_to_sparse_voxel_grid(voxels, threshold=0.5)

def generate_pcd_from_depth(depth, kuf, kvf):
    '''
    Given a depth image and camera intrinsics, convert to a point cloud
    and save as PCD file.
    '''
    # Convert depth to a point cloud and store as PCD.
    height = depth.shape[0]
    width = depth.shape[1]

    # Go through each nonzero and convert to a 3D point using camera matrix.
    mask = np.where(depth > 0)
    x = mask[1]
    y = mask[0]
    norm_x = (x.astype(np.float32)-(width*0.5)) / (width*0.5)
    norm_y = (y.astype(np.float32)-(height*0.5)) / (height*0.5)
    world_x = norm_x * depth[y,x] / kuf
    world_y = norm_y * depth[y,x] / kvf
    world_z = -depth[y,x]
    points = np.vstack((world_x, -world_y, world_z)).T # Negative on y because of image matrix layout.
    return points

def generate_point_clouds_from_mesh(object_mesh, object_name, num_poses_per_obj,
                                    noise_sigma=0.001):

    '''
    Generate point clouds from object triangular mesh.
    '''

    import pyrender

    # Setup the scene.
    scene = pyrender.Scene()

    # Camera/Light
    cam = pyrender.PerspectiveCamera(yfov=0.820305, aspectRatio=(4.0/3.0))
    # Get some camera instrinsics to do depth to point cloud conversion.
    kuf = cam.get_projection_matrix()[0][0]
    kvf = cam.get_projection_matrix()[1][1]
    camera_pose = np.eye(4)
    scene.add(cam, pose=camera_pose)
    light_pose = [
        [1., 0., 0., 0.],
        [0., 1., 0., 0.],
        [0., 0., 1., 10.],
        [0., 0., 0., 1.]
        ]
    scene.add(pyrender.DirectionalLight(), pose=light_pose)

    # Setup renderer.
    r = pyrender.OffscreenRenderer(viewport_width=1640, viewport_height=1480, point_size=1.0)

    m = pyrender.Mesh.from_trimesh(object_mesh)
    mesh_pose = [
        [1., 0., 0., 0.],
        [0., 1., 0., 0.],
        [0., 0., 1., -1.5],
        [0., 0., 0., 1.]
        ]
    mesh_node = pyrender.Node(mesh=m, matrix=mesh_pose)
    scene.add_node(mesh_node)

    # Do a bunch of orientations for each object.
    all_generated_point_clouds = []
    for rot_num in range(num_poses_per_obj):
        object_pose_name = object_name + "_" + str(rot_num)

        # Generate a random transformation.
        random_pose = trimesh.transformations.random_rotation_matrix()
        random_pose[2][3] = -1.5 # Move to spot we want.

        # Update the pose.
        scene.set_pose(mesh_node, pose=random_pose)

        # Render.
        color, depth = r.render(scene)

        import matplotlib.pyplot as plt
        # print(color)
        # plt.imshow(color)
        # plt.show()

        # Add noise to depth image.
        noise = np.random.normal(0.0, noise_sigma, depth.shape)
        nonzeros = np.nonzero(depth)
        depth[nonzeros] = depth[nonzeros] + noise[nonzeros]

        # Save PCD.
        points = generate_pcd_from_depth(depth, kuf, kvf)
        all_generated_point_clouds.append(points)

    # Remove object.
    scene.remove_node(mesh_node)
    # Cleanup
    r.delete()

    return all_generated_point_clouds


import time
from CollectionModule.mongo_db_handler import collection_manager
from bson import ObjectId

class clean_data(object):

    #Gallery card transaction note
    '''
    Title => Account transfer of xxx$
    Subtitle => xxx$ transfered from sender_name to recieved_name
    Button => Click here to view more
    username,password => [{'save_variables': [{'user_id': 'bla_bla', 'balance': ''}]}]
    '''

    def generate_gallery_card_from_list(self, list_name, data):
        gal_card =   {
            "attachment":{
            "type":"template",
            "payload":{
                "template_type":"generic",
                    "elements":[
                    ]
                }
            }
        }
        for entry in data:
            element_list = gal_card["attachment"]["payload"]["elements"]
            element = {
                "title": "{0}",
                "buttons": [
                    {
                        "type": "web_url",
                        "url": "https://google.com",
                        "title": "click here to view more"
                    }
                ]
            }

            element["title"] = entry
            element_list.append(element)

        return [gal_card]

    def generate_gallery_card_transaction(self, transactions):
        gal_card =   {
            "attachment":{
            "type":"template",
            "payload":{
                "template_type":"generic",
                    "elements":[
                    ]
                }
            }
        }

        for trans in transactions:
            element_list = gal_card["attachment"]["payload"]["elements"]
            title = "Account transfer of ${0}".format(trans["amount"])
            subtitle = "${0} transfered from {1} to {2} on {3} at {4}".format(
                trans["amount"],
                trans["sender_name"],
                trans["receiver_name"],
                trans["date"],
                trans["time"])

            element = {
                "title": "{0}",
                "subtitle": "{1}",
                "buttons": [
                    {
                        "type": "web_url",
                        "url": "https://google.com",
                        "title": "click here to view more"
                    }
                ]
            }

            element["title"] = title
            element["subtitle"] = subtitle
            element_list.append(element)
        return [gal_card]

    def get_transactions(self, transactions, amounts):
        trans = []
        db = collection_manager("banks", "users")
        db_trans = collection_manager("banks", "transactions")
        if amounts == 0:
            amounts = 10
        for transaction in reversed(transactions):
            amounts = amounts - 1
            transaction = db_trans.find_query({"_id": ObjectId(transaction)})[0]
            sender_identity = transaction["sender"]
            receiver_identity = transaction["receiver"]
            amount = transaction["amount"]
            send_Obj = db.find_query({"_id": sender_identity})[0]
            receive_obj = db.find_query({"_id": receiver_identity})[0]
            send_name = send_Obj["fullname"]
            receive_name = receive_obj["fullname"]
            data_dict = {}
            data_dict["sender_name"] = send_name
            data_dict["receiver_name"] = receive_name
            data_dict["amount"] = amount
            data_dict["date"] = time.strftime('%Y-%m-%d', time.localtime(transaction["timestamp"]))
            data_dict["time"] = time.strftime('%H:%M:%S', time.localtime(transaction["timestamp"]))
            trans.append(data_dict)
            if amounts == 0:
                break
        return self.generate_gallery_card_transaction(trans)

    def generate_branch_card(self, branchArray):
        gal_card =   {
            "attachment":{
            "type":"template",
            "payload":{
                "template_type":"generic",
                    "elements":[
                    ]
                }
            }
        }
        for branch in branchArray:
            element_list = gal_card["attachment"]["payload"]["elements"]
            title = "{0} branch located at of {1}".format(branch["name"], branch["location"])
            subtitle = "branch ID is: {0}".format(str(branch["branch_id"]))

            element = {
                "title": "{0}",
                "subtitle": "{1}",
                "buttons": [
                    {
                        "type": "web_url",
                        "url": "https://google.com",
                        "title": "click here to view more"
                    }
                ]
            }

            element["title"] = title
            element["subtitle"] = subtitle
            element_list.append(element)
        return [gal_card]


import string

import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.firefox.options import Options
from bs4 import BeautifulSoup

opts = Options()
opts.headless = True
driver = webdriver.Firefox(options=opts)

URL = "https://apps.ualberta.ca/catalogue/search/results?keywords="

pages = 101  # 101  # soup.find('button', class_='') REVISE TO DYNAMICALLY GRAB MAX PAGE NUMBER
course_labels = list()
course_names = list()
course_credits = list()

nameAreaList = list()
priceAreaList = list()

pageNumber = 1

while pageNumber < (pages + 1):
    URL = 'https://apps.ualberta.ca/catalogue/search/results?keywords=&page=' + str(pageNumber)

    driver.get(URL)
    soup = BeautifulSoup(driver.page_source, features="html.parser")
    items = soup.find('div', class_='card')
    for n in items:
        if (n != "\n"):
            name = n.find('a')  # finding the area with the product names
            # category_sing = nameBig.find('a')  # finding the actual names
            description = n.find('p')
            nmcredits = n.find('b')
            r = name
            if r is not None:
                r = r.text  # [str(i.string) for str(i) in category_sing]
            s = description
            if s is not None:
                s = s.text  # [str(p.string) for str(p) in price_sing]
            c = nmcredits
            if c is not None:
                c = c.text

            course_labels.append(r)
            course_names.append(s)
            course_credits.append(c)
    pageNumber += 1

d = {'Names': course_labels, 'Credits': course_credits, 'Descriptions': course_names}

df = pd.DataFrame.from_dict(data=d)
df.to_csv('classes-uofa.csv', index=False, encoding='utf-8')

driver.close()


"""
RCM-specific tools to include h5 data in xmf files
"""
import h5py as h5
import kaipy.kaixdmf as kxmf

"""
def AddGrid(fname,Geom,iDims,coordStrs):

	for coordStr in coordStrs:
		xC = et.SubElement(Geom,"DataItem")
		xC.set("Dimensions",iDims)
		xC.set("NumberType","Float")
		xC.set("Precision","4")
		xC.set("Format","HDF")

		text = fname+":/"+coordStr
		xC.text = text
"""

def addRCMGeom(Geom, dimInfo, rcmInfo, sID):
	#Unpack
	rcmh5fname = rcmInfo['rcmh5fname']

	sIDstr = "Step#" + str(sID)

	gridVars = dimInfo['gridVars']  # Expect something like rcmxmin,rcmymin,alamc
	gDims = dimInfo['gDims']  # Someone before us should set kji dims
	#gDims[1] -= 2  # Remove 2 j vals (jWrap)

	#all
	dSetDimStr = ' '.join([str(v) for v in gDims])

	hsDimStr = "3 2"  # row, col
	startStr = "2 0"
	strideStr = "1 1"
	numStr = "359 180"
	origDSetDimStr = str(gDims[0])

	fileText = "{}:/{}/{}".format(rcmh5fname,sIDstr,'rxmxmin')
	kxmf.addHyperslab_DI(Geom, dSetDimStr, hsDimStr, startStr, strideStr, numStr, origDSetDimStr, fileText)

	fileText = "{}:/{}/{}".format(rcmh5fname,sIDstr,'rcmymin')
	kxmf.addHyperslab_DI(Geom, dSetDimStr, hsDimStr, startStr, strideStr, numStr, origDSetDimStr, fileText)

	hsDimStr = "3 1"
	startStr = "0"
	strideStr = "1"
	numStr = "140"
	fileText = "{}:/{}/{}".format(rcmh5fname,sIDstr,'alamc')
	kxmf.addHyperslab_DI(Geom, dSetDimStr, hsDimStr, startStr, strideStr, numStr, origDSetDimStr, fileText)

def addRCMVars(Grid, dimInfo, rcmInfo, sID):  # Used with the mhdrcm presets to add links to given RCM variables

	sIDstr = "Step#" + str(sID)

	trg_vDims = dimInfo['vDims']  # target file var dims (probably mhdrcm or rcm)
	trg_vDimStr = ' '.join([str(v) for v in trg_vDims])
	trg_nDims = len(trg_vDims)
	rcmh5fname = rcmInfo['rcmh5fname']
	rcmVars = rcmInfo['rcmVars'] # List of rcm.h5 variables we want in mhdrcm.xmf
	rcmKs = rcmInfo['rcmKs'] # List if rcm.h5 k values for 3d rcm.h5 vars

	rcm5 = h5.File(rcmh5fname,'r')
	if 'Nk' not in rcmInfo.keys():
		rcmInfo['Nj'], rcmInfo['Ni'] = rcm5[sIDstr]['aloct'].shape
		rcmInfo['Nk'] = rcm5[sIDstr]['alamc'].shape[0]
		#print("Adding Nk to rcmInfo")
	Ni = rcmInfo['Ni']
	Nj = rcmInfo['Nj']
	Nk = rcmInfo['Nk']

	for vName in rcmVars:
		doHyperslab = False
		r_var = rcm5[sIDstr][vName]
		r_vShape = r_var.shape
		r_vDimStr = " ".join([str(d) for d in r_vShape])
		r_nDims = len(r_vShape)
		dimTrim = 0

		if (r_nDims == 2 and trg_vDims[0] < Nj) or (r_nDims == 3 and trg_vDims[1] < Nj):
			doHyperslab = True
			dimTrim = (Nj - trg_vDims[0]) if trg_nDims == 2 else (Nj - trg_vDims[1])  # Shortening j-dir

		if r_nDims == 2 and doHyperslab == False:  # Easy add
				#print("Adding " + vName)
				kxmf.AddData(Grid,rcmh5fname, vName,"Cell",trg_vDimStr,sID)
				continue

		#Add data as a hyperslab
		if doHyperslab:
			#Do 2D stuff. If 3D needed, will be added in a sec
			dimStr = "3 2"
			startStr = "{} 0".format(dimTrim)
			strideStr = "1 1"
			numStr = "{} {}".format(Nj-dimTrim, Ni)
			text = "{}:/{}/{}".format(rcmh5fname,sIDstr,vName)

			if r_nDims == 2:
				kxmf.addHyperslab_Attr(Grid,vName,trg_vDimStr,dimStr,startStr,strideStr,numStr,r_vDimStr,text)
				continue
			elif r_nDims == 3:
				dimStr = "3 3"
				strideStr = str(Nk+1) + " 1 1"
				numStr = "1 {} {}".format(Nj-dimTrim,Ni)
				for k in rcmKs:
					startStr = "{} {} 0".format(k,dimTrim)
					vName_k = vName + "_k{}".format(k)
					kxmf.addHyperslab_Attr(Grid,vName_k,trg_vDimStr,dimStr,startStr,strideStr,numStr,r_vDimStr,text)


import bpy
import cv2
import os
import numpy as np
def initialize_avatar():
    # Import the base avatar
    for o in bpy.context.scene.objects:
        if o.name == 'Cube':
            o.select_set(True)
        else:
            o.select_set(False)
    # Call the operator only once
    bpy.ops.object.delete()
    light1 = bpy.data.lights.new(name='light',type='POINT')
    light_object1 = bpy.data.objects.new(name="light", object_data=light1)
    light_object1.location=(-0.210575, -6.56262, 7.41522)
    light1.energy=1000
    light2 = bpy.data.lights.new(name='light.001',type='POINT')
    light_object2 = bpy.data.objects.new(name="light.001", object_data=light2)
    light_object2.location=(-0.210575, -6.56262, 4.99445)
    light2.energy=1000
    light3 = bpy.data.lights.new(name='light.002',type='POINT')
    light_object3 = bpy.data.objects.new(name="light.002", object_data=light3)
    light_object3.location=(-0.210575, -6.56262, 1.87751)
    light3.energy=1000
    bpy.ops.import_scene.fbx( filepath = "3D_Model/MaleModelSankit.fbx")
    armature = bpy.context.scene.objects['metarig']
    obj = bpy.context.selected_objects[0]
    obj.animation_data_clear()
    scene=bpy.context.scene
    bpy.ops.object.camera_add(location=(-0.0360, -12.9774, 4.5951),
                            rotation=(1.5340, -0.0015,-0.0098))

    bpy.context.collection.objects.link(light_object1)
    bpy.context.collection.objects.link(light_object2)
    bpy.context.collection.objects.link(light_object3)
    bpy.context.view_layer.objects.active = light_object1
    bpy.context.view_layer.objects.active = light_object2
    bpy.context.view_layer.objects.active = light_object3
    return armature, scene

def two_d_pose_image(y, filename, armature, scene):
    """
    Merge ans save two 2d images for comparison.
    (Regressed 2d points and ground truth 2d points)
    Input is quaternions.
    """
    ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
    # initialize the camera parameters
    R=np.array([[1.0000, -0.0098,  0.0015],[0.0011, -0.0368, -0.9993],[0.0099,  0.9993, -0.0368]])
    T=np.array([[-0.0987], [4.1149], [13.1372]])
    f=50.0
    k=np.zeros((3,1))
    p=np.zeros((2,1))
    c=np.array([[125.0],[125.0]])

    # Rotate the avatar using the regressed quaternions
    final_output=[]
    obj = bpy.context.selected_objects[0]
    obj.animation_data_clear()
    poses=y
    for bone in armature.pose.bones:
        if bone.name in poses:
            bone.rotation_quaternion.w=poses[bone.name][0]
            bone.rotation_quaternion.x=poses[bone.name][1]
            bone.rotation_quaternion.y=poses[bone.name][2]
            bone.rotation_quaternion.z=poses[bone.name][3]
            bone.keyframe_insert(data_path="rotation_quaternion" ,frame=0)
    bpy.context.scene.frame_set(0)

    bpy.context.scene.camera = bpy.data.objects['Camera.001']
    scene.render.image_settings.file_format = 'PNG'
    scene.render.filepath = ROOT_DIR + '/test.png'
    scene.render.resolution_x = 480 #perhaps set resolution in code
    scene.render.resolution_y = 640
    bpy.context.scene.render.engine = 'CYCLES'
    # bpy.context.scene.render.engine = 'BLENDER_EEVEE'
    bpy.ops.render.render(write_still=True)

    img=cv2.imread(ROOT_DIR + '/test.png')
    if not os.path.exists(ROOT_DIR + '/test_outputs'):
        os.makedirs(ROOT_DIR + '/test_outputs')
    cv2.imwrite(ROOT_DIR + "/test_outputs/"+filename, img)
    os.remove(ROOT_DIR + '/test.png')


from celery_app import celery_app, forward_to_fastapi
import logging

logging.basicConfig(level=logging.DEBUG)

def main():
    commentary = "Rodrigo"
    result = forward_to_fastapi.apply_async(args=[commentary])

    print("Sent the task.")
    response = result.get(timeout=30)
    print(f"Task Result: {response}")
    print(f"Task State: {result.state}")

if __name__ == "__main__":
    main()


import os
import random
import numpy as np
import torch.nn.functional as F
from yolov5.utils.general import *
from yolov5.utils.boxes import xywh2xyxy
from yolov5.utils.plots import plot_images_boxes_and_masks
from yolov5.data import LoadImagesAndLabelsAndMasks, create_dataloader
import cv2
from yolov5.utils.general import init_seeds

names = ["balloon"]
seed = 2
init_seeds(seed)
colors = [[random.randint(0, 255) for _ in range(3)] for _ in range(len(names))]

with open("data/hyps/hyp.scratch.yaml") as f:
    hyp = yaml.load(f, Loader=yaml.FullLoader)  # load hyps

save = False
save_dir = "/d/projects/yolov5/data/play_phone0115/show_labels"

if save and not os.path.exists(save_dir):
    os.mkdir(save_dir)

dataloader, dataset = create_dataloader(
            # '/d/baidubase/COCO/val_yolo/images/train',
            # 'data/license_plates/images/train/',
            # '/d/projects/research/yolov5/data/coco/train2017.txt',
            '/d/projects/research/yolov5/data/coco/val2017.txt',
            # "/home/laughing/code/yolov5-6/data/seg/coco_02/images/train",
            imgsz=640,
            batch_size=4,
            stride=32,
            augment=False,
            shuffle=True,
            rank=-1,
            hyp=hyp,
            mask_head=True
)
cv2.namedWindow("mosaic", cv2.WINDOW_NORMAL)

for i, (imgs, targets, paths, _, masks) in enumerate(dataloader):
    #     print(targets)
    # print(targets)

    # show masks
    # mxywh = targets[:, 2:].cpu()
    # mxyxy = xywh2xyxy(mxywh) * torch.tensor((640, 640))[[0, 1, 0, 1]]
    # maskssss = masks.permute(1, 2, 0).contiguous()
    # for k in range(maskssss.shape[-1]):
    #     c1 = (int(mxyxy[k][0]), int(mxyxy[k][1]))
    #     c2 = (int(mxyxy[k][2]), int(mxyxy[k][3]))
    #     img = maskssss[:, :, k].cpu().numpy().astype(np.uint8) * 255
    #     cv2.rectangle(img, c1, c2, (255, 255, 255), -1, cv2.LINE_AA)  # filled
    #     cv2.imshow('p', cv2.resize(img, (640, 640)))
    #     if cv2.waitKey(0) == ord('q'):
    #         break
    # exit()

    if getattr(dataset, 'downsample_ratio', 1) != 1 and masks is not None:
        masks = F.interpolate(
            masks[None, :],
            (640, 640),
            mode="bilinear",
            align_corners=False,
        ).squeeze(0)

    result = plot_images_boxes_and_masks(images=imgs, targets=targets, paths=paths, masks=masks)
    cv2.imshow("mosaic", result[:, :, ::-1])
    if cv2.waitKey(0) == ord("q"):  # q to quit
        break
    continue
    # imgs = imgs.numpy().astype(np.uint8).transpose((1, 2, 0))
    # imgs = cv2.cvtColor(imgs, cv2.COLOR_BGR2RGB)
    # targets = targets.numpy()
    # temp = targets[:, 2:6].copy()
    # targets[:, 2:6] = xywh2xyxy(temp * 960)
    # for idx, cls, *xyxy in targets:
    #     label = '%s' % (names[int(cls)])
    #     plot_one_box(xyxy, imgs, label=label, color=colors[int(cls)], line_thickness=2)
    # # print(targets[targets[:, 0] == 0])
    # if save and len(targets[targets[:, 1] == 0]) > 0:
    #     cv2.imwrite(os.path.join(save_dir, os.path.split(paths)[-1]), imgs)
    # cv2.imshow('p', imgs)
    # cv2.waitKey(0)


from django.shortcuts import render,HttpResponse,redirect
from home.models import Contact_Us , Doctor
from datetime import datetime
from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.auth import authenticate,login,logout

# Create your views here.
def index (request):
    return render(request, 'index.html')

def contactus (request):
    if (request.method =="POST") :
        name = request.POST.get('name')
        email = request.POST.get('email')
        phone = request.POST.get('phone')
        desc = request.POST.get('desc')
        contact=Contact_Us(
            name=name,
            email=email,
            phone=phone,
            description=desc,
            date=datetime.today()
            )
        contact.save()
        messages.success(request, ' THANK YOU! Your information is received')
    return render(request, 'contactus.html')

def aboutus (request):
    return render(request,'aboutus.html')

def doctors (request):
    doc=Doctor.objects.all()
    return render(request,'doctors.html',{"data":doc})


import sys
import os
sys.path.append(os.path.abspath("./astmt"))

import typing as t

import numpy as np
import pandas as pd
import gdown
import torch
from PIL import Image

from astmt.experiments.dense_predict.pascal_resnet import config as pascal_config
from astmt.experiments.dense_predict.nyud_resnet import config as nyud_config

from astmt.fblib.util.mypath import Path

device = "cuda" if torch.cuda.is_available() else "cpu"
module_name = 'astmt_inference'
model_params: t.Dict[str, t.Any] = pd.read_json("inference/astmt_model_params.json")

# models marked with SSF have been trained by us and will be attempted to be downloaded from the web
# you can use your own trained models by making an appropriate entry in the model_params dictionary
# the key will of your entry will be considered the "model_name" when running an inference task

SSF_model_urls = {
    # 'pascal_edge_scratch_SSF': "https://drive.google.com/u/0/uc?id=1dOOIZFJUfiegnfeaG-YdX2JwAOxqRNT9",
    'pascal_edge_scratch_SSF': "https://drive.google.com/u/0/uc?id=17rYmh4-CzgsLOptDDmRGDvdjOpDZ8Aym",
    'pascal_semseg_scratch_SSF':"https://drive.google.com/u/0/uc?id=1-ERrxEWSU2fS2elra8I2gItnGS40Nc0P",
    'pascal_normals_scratch_SSF':"https://drive.google.com/u/0/uc?id=1-3XuBx-g8-vEpa3_84sQ97i1wNhDQWBU",
    'pascal_edge_imagenet_SSF':"https://drive.google.com/u/0/uc?id=1-4UqQQXi-C0f7aMFIbEKXNGZuxgcweba",
    'pascal_semseg_imagenet_SSF':"https://drive.google.com/u/0/uc?id=1-5RRRVLemxNTAo_neSNMbXEZk8UxPaqW",
    'pascal_normals_imagenet_SSF':"https://drive.google.com/u/0/uc?id=1--3za_W8vP95XhR459DVlSCoUya_P5p5",
    'nyud_edge_scratch_SSF':"https://drive.google.com/u/0/uc?id=1-UTjE0lMFOkoEw-b3FGCSdpY5AA9TtGL",
    'nyud_semseg_scratch_SSF':"https://drive.google.com/u/0/uc?id=1-ey7AbX7P1wiaQBYSoacWPp7BOHXZXRq",
    'nyud_normals_scratch_SSF':"https://drive.google.com/u/0/uc?id=1-XGar4UVorpqH3ZxGipDqYFA_1AisgT-",
    'nyud_edge_imagenet_SSF':"https://drive.google.com/u/0/uc?id=1-KXR-ZUlYaNbD4jFRnpD_2TTFrk_zW03",
    'nyud_semseg_imagenet_SSF':"https://drive.google.com/u/0/uc?id=1-awtoMPc9rYeytxQazlV7QDIdaA5iLch",
    'nyud_normals_imagenet_SSF':"https://drive.google.com/u/0/uc?id=1-UZG6basLaa8zAIM30fXrjNOAs4RJb0z",
}

layer_choices = ['high', 'low']

viable_models = model_params.keys()
default_model = 'pascal_edge_scratch_SSF'

model_ = None
currently_selected_model:str = None
cfg = None
transformer = None

def loader(path: str):
    return np.array(Image.open(path).convert('RGB')).astype(np.float32)

def preprocessor(data_):

    tmp = transformer({'image': data_})
    return tmp['image'].unsqueeze(0).to(device)  # def sus, that we have to unsqueeze; in astmt main project this was not necessary

def model_call(data_, layer: str):

    with torch.no_grad():
        if layer == 'high' or layer is None:
            return {task: model_.forward(data_, task)[1][0][:32] for task in model_.tasks} # model.forward return is a tuple with [0] being the output
                                                                            # and [1] the features
                                                                            # features is at this point a 4dim tensor with
                                                                            # 64 entries, last 32 belonging to low level output,
                                                                            # first 32 belonging to high level output
        elif layer == 'low':
            return {task: model_.forward(data_, task)[1][0][32:] for task in model_.tasks}
        else:
            raise NotImplementedError("layer could not be found\n"
                                "available layers: ", layer_choices)

def postprocessor(data_: t.Dict[str, torch.Tensor], compress=True):

    # TODO?: no compression for now

    if compress:
        return {task: np.float16(
            torch.nn.AvgPool2d(3)(features.unsqueeze(0))[0].to('cpu')
                                ) for task, features in data_.items()}

    else:
        return {task: np.float32(features.to('cpu')) for task, features in data_.items()}

def generate_file_name(old_file_name, task="no_task"):

    assert task != "no_task" or currently_selected_model == "imagenet_pretr"  # a task has to be specified, except with imagenet pretr

    return old_file_name.split(".")[0] + '_features' + \
           ('_' + task if currently_selected_model != "imagenet_pretr" else "") +\
                                                                             '.npy'

def saver(data_: t.Dict[str, np.ndarray], path: str, file_name: str) -> t.NoReturn:

    for task in data_.keys():
        full_path = os.path.join(path, module_name, currently_selected_model)
        if not os.path.isdir(full_path):
            os.makedirs(full_path)
        np.save(os.path.join(full_path, generate_file_name(file_name, task)), data_[task]) # first 32 are high features

def select_model(model_name: str):

    global model_, cfg, transformer, currently_selected_model

    currently_selected_model = model_name
    if 'SSF' in model_name:
            _download_SSF_model(model_name)

    parse_string = _create_parse_string(model_params[model_name])

    if 'nyud' in model_name:
        cfg = nyud_config.create_config(parse_string)
        model_ = nyud_config.get_net_resnet(cfg)
        _, _, transformer = nyud_config.get_transformations(cfg)
    elif 'pascal' in model_name or model_name == "imagenet_pretr":
        cfg = pascal_config.create_config(parse_string)
        model_ = pascal_config.get_net_resnet(cfg)
        _, _, transformer = pascal_config.get_transformations(cfg)
    else:
        raise NotImplementedError("model name should either contain 'nyud' or 'pascal' or be 'imagenet_pretr")

    model_.to(device)
    model_.eval()

def get_features_by_image_path(path_to_file: str):
    pass

def get_features_by_image_data(img_data: np.ndarray):
    pass

def _download_SSF_model(model_name: str):

    url = SSF_model_urls[model_name]
    save_dir = os.path.join(Path.exp_dir(), "SSF")
    tmp_file_name = os.path.join("tmp", model_name + ".zip")

    if not os.path.isdir(save_dir):
        os.makedirs(save_dir)
    if not os.path.isdir("tmp"):
        os.mkdir("tmp")

    if not os.path.isfile(tmp_file_name):
        gdown.download(url, tmp_file_name, quiet=False)
        gdown.extractall(tmp_file_name, save_dir)
    else:
        print(tmp_file_name, "already exists, skipping download")

def _create_parse_string(cfg: t.Dict):

    args = []
    for key, value in cfg.items():
        args.append("--" + key)

        if value != "":
            if isinstance(value, list):
                for arg in value:

                    args.append(str(arg))
            else:
                args.append(str(value))
    return args


import net
import numpy as np
import torch
import torch.nn as nn
# import read_data
import datasets
from torch import optim
from criterion import PixelLinkLoss
import loss
import config
import postprocess
from torch.utils.data import DataLoader
from torch.utils.data.sampler import RandomSampler, WeightedRandomSampler
import os
import cv2
import time
import argparse
import ImgLib.ImgShow as ImgShow
import ImgLib.ImgTransform as ImgTransform
import moduletest.test_postprocess as test_postprocess
from test_model import test_on_train_dataset

# os.environ['CUDA_LAUNCH_BLOCKING'] = '1'
parser = argparse.ArgumentParser(description='')
parser.add_argument('--train', type=bool, default=False, help='True for train, False for test') # default for test
parser.add_argument('--retrain', type=bool, default=False, help='True for retrain, False for train') # default for test
# parser.add_argument('change', metavar='N', type=int, help='an integer for change')
args = parser.parse_args()

def weight_init(m):
    if isinstance(m, nn.Conv2d):
        nn.init.xavier_uniform_(m.weight.data)
        nn.init.constant_(m.bias.data, 0)

def retrain():
    dataset = datasets.PixelLinkIC15Dataset(config.train_images_dir, config.train_labels_dir)
    sampler = WeightedRandomSampler([1/len(dataset)]*len(dataset), config.batch_size, replacement=True)
    dataloader = DataLoader(dataset, batch_size=config.batch_size, sampler=sampler)
    my_net = net.Net()
    if config.gpu:
        device = torch.device("cuda:0")
        my_net = my_net.cuda()
        if config.multi_gpu:
            my_net = nn.DataParallel(my_net)
    else:
        device = torch.device("cpu")
    my_net.load_state_dict(torch.load(config.saving_model_dir + '%d.mdl' % config.retrain_model_index))
    optimizer = optim.SGD(my_net.parameters(), lr=config.retrain_learning_rate2, \
                            momentum=config.momentum, weight_decay=config.weight_decay)
    optimizer2 = optim.SGD(my_net.parameters(), lr=config.retrain_learning_rate, \
                            momentum=config.momentum, weight_decay=config.weight_decay)
    train(config.retrain_epoch, config.retrain_model_index, dataloader, my_net, optimizer, optimizer2, device)

def train(epoch, iteration, dataloader, my_net, optimizer, optimizer2, device):
    for i in range(epoch):
        for i_batch, sample in enumerate(dataloader):
            start = time.time()
            images = sample['image'].to(device)
            # print(images.shape, end=" ")
            pixel_masks = sample['pixel_mask'].to(device)
            neg_pixel_masks = sample['neg_pixel_mask'].to(device)
            link_masks = sample['link_mask'].to(device)
            pixel_pos_weights = sample['pixel_pos_weight'].to(device)

            out_1, out_2 = my_net.forward(images)
            loss_instance = PixelLinkLoss()
            # print(out_2)

            pixel_loss_pos, pixel_loss_neg = loss_instance.pixel_loss(out_1, pixel_masks, neg_pixel_masks, pixel_pos_weights)
            pixel_loss = pixel_loss_pos + pixel_loss_neg
            link_loss_pos, link_loss_neg = loss_instance.link_loss(out_2, link_masks)
            link_loss = link_loss_pos + link_loss_neg
            losses = config.pixel_weight * pixel_loss + config.link_weight * link_loss
            print("iteration %d" % iteration, end=": ")
            print("pixel_loss: " + str(pixel_loss.tolist()), end=", ")
            # print("pixel_loss_pos: " + str(pixel_loss_pos.tolist()), end=", ")
            # print("pixel_loss_neg: " + str(pixel_loss_neg.tolist()), end=", ")
            print("link_loss: " + str(link_loss.tolist()), end=", ")
            # print("link_loss_pos: " + str(link_loss_pos.tolist()), end=", ")
            # print("link_loss_neg: " + str(link_loss_neg.tolist()), end=", ")
            print("total loss: " + str(losses.tolist()), end=", ")
            if iteration < 100:
                optimizer.zero_grad()
                losses.backward()
                optimizer.step()
            else:
                optimizer2.zero_grad()
                losses.backward()
                optimizer2.step()
            end = time.time()
            print("time: " + str(end - start))
            if (iteration + 1) % 200 == 0:
                # if args.change:
                #     saving_model_dir = config.saving_model_dir3
                # else:
                saving_model_dir = config.saving_model_dir
                torch.save(my_net.state_dict(), saving_model_dir + str(iteration + 1) + ".mdl")
            iteration += 1

def main():
    dataset = datasets.PixelLinkIC15Dataset(config.train_images_dir, config.train_labels_dir)
    sampler = WeightedRandomSampler([1/len(dataset)]*len(dataset), config.batch_size, replacement=True)
    dataloader = DataLoader(dataset, batch_size=config.batch_size, sampler=sampler)
    # dataloader = DataLoader(dataset, config.batch_size, shuffle=True)
    my_net = net.Net()

    if config.gpu:
        device = torch.device("cuda:0")
        my_net = my_net.cuda()
        if config.multi_gpu:
            my_net = nn.DataParallel(my_net)
    else:
        device = torch.device("cpu")

    # nn.init.xavier_uniform_(list(my_net.parameters()))
    my_net.apply(weight_init)
    optimizer = optim.SGD(my_net.parameters(), lr=config.learning_rate, momentum=config.momentum, weight_decay=config.weight_decay)
    # if args.change:
    optimizer2 = optim.SGD(my_net.parameters(), lr=config.learning_rate2, momentum=config.momentum, weight_decay=config.weight_decay)
    # else:
    #     optimizer2 = optim.SGD(my_net.parameters(), lr=config.learning_rate, momentum=config.momentum, weight_decay=config.weight_decay)

    iteration = 0
    train(config.epoch, iteration, dataloader, my_net, optimizer, optimizer2, device)

if __name__ == "__main__":
    if args.retrain:
        retrain()
    elif args.train:
        main()
    else:
        test_on_train_dataset()
        # test_model()


import jpype
import jpype.imports
from jpype.types import *

def startJVM(*args, **kwargs):
    """
    Starts a jpype JVM if it isn't already started.
    If it is started, args and kwargs will be ignored.
    """
    try:
        jpype.startJVM(*args, **kwargs)
    except OSError as e:
        if str(e) != 'JVM is already started':
            raise e

def list_to_arraylist(l):
    """
    Converts a Python list to a jpype java ArrayList
    """
    startJVM()
    from java.util import ArrayList

    if isinstance(l, ArrayList):
        return l
    elif l is None:
        return JObject(None, ArrayList)
    else:
        al = ArrayList(len(l))
        al.extend(l)
        return al

def dict_to_hashmap(d):
    """
    Converts a python dict to a jpyp Java HashMap.
    """
    startJVM()
    from java.util import HashMap
    if isinstance(d, HashMap):
        return d
    elif d is None:
        return JObject(None, HashMap)
    else:
        hm = HashMap()
        for k, v in d.items():
            hm.put(k, v)
        return hm


import torch
import torchvision
from torchvision.datasets import FashionMNIST
from torch.utils.data import DataLoader
from torchvision import transforms
import numpy as np
import matplotlib.pyplot as plt
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable

batch_size = 50

dataTransform = transforms.ToTensor()
trainData = FashionMNIST(root='./data', train=True, download=True, transform=dataTransform)
trainLoader = DataLoader(trainData, batch_size=batch_size, shuffle=True)
testData = FashionMNIST(root='./data', train=False,  download=True, transform=dataTransform)
testLoader = DataLoader(testData, batch_size=batch_size, shuffle=True)

classes = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
           'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 8, 3, padding=1)
        self.pool = nn.MaxPool2d(2,2)
        self.conv2 = nn.Conv2d(8,16,3, padding=1)
        self.fc1 = nn.Linear(7*7*16,75)
        self.dropout = nn.Dropout(0.2)
        self.fc2 = nn.Linear(75,10)

    def forward(self, x):

        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(x.size(0), -1)
        x = F.relu(self.fc1(x))
        x = self.dropout(x)
        x = F.relu(self.fc2(x))
        return x

net = Net()

modelDirectory = 'saved/'

modelName = input("\nEnter the Model Name (default savedModel):")
if modelName=='':
    modelName="savedModel"
modelName+=".pt"

net.load_state_dict(torch.load( modelDirectory+modelName))

test_loss = torch.zeros(1)
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))

net.eval()

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

correct = 0
total = 0

for batch_i, data in enumerate(testLoader):

    inputs, labels = data

    with torch.no_grad():

        outputs = net(inputs)
        loss = criterion(outputs, labels)
        test_loss = test_loss + ((torch.ones(1) / (batch_i + 1)) * (loss.data - test_loss))
        _, predicted = torch.max(outputs.data, 1)
        correct = np.squeeze(predicted.eq(labels.data.view_as(predicted)))

        for i in range(batch_size):
            label = labels.data[i]
            class_correct[label] += correct[i].item()
            class_total[label] += 1

print('Test Loss: {:.6f}\n'.format(test_loss.numpy()[0]))

for i in range(10):
    if class_total[i] > 0:
        print('Test Accuracy of %5s: %2d%% (%2d/%2d)' % (
            classes[i], 100 * class_correct[i] / class_total[i],
            np.sum(class_correct[i]), np.sum(class_total[i])))
    else:
        print('Test Accuracy of %5s: ' % (classes[i]))

print('\nTest Accuracy (Overall): %2d%% (%2d/%2d)' % (
    100. * np.sum(class_correct) / np.sum(class_total),
    np.sum(class_correct), np.sum(class_total)))

dataiter = iter(testLoader)
images, labels = next(dataiter)

preds = np.squeeze(net(images).data.max(1, keepdim=True)[1].numpy())
images = images.numpy()

fig = plt.figure(figsize=(30, 6))
for idx in np.arange(batch_size//5*2):
    ax = fig.add_subplot(2, batch_size//5, idx+1, xticks=[], yticks=[])
    ax.imshow(np.squeeze(images[idx]), cmap='gray')
    ax.set_title("{} ({})".format(classes[preds[idx]], classes[labels[idx]]),
                 color=("green" if preds[idx]==labels[idx] else "red"))
plt.tight_layout()
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Tue Dec 19 16:57:00 2017

@author: Maitreya
"""
import cv2
import numpy as np

def grabcut_func(img1, img2):
	resized_img = cv2.resize(img1, (0, 0), fx=0.2, fy=0.2)
	resized_img1 = cv2.resize(img2, (0, 0), fx=0.2, fy=0.2)
	print ("Image1...")
	mask = np.zeros(resized_img.shape[:2],np.uint8)
	bgdModel = np.zeros((1,65),np.float64)
	fgdModel = np.zeros((1,65),np.float64)
	rect = (10,130,resized_img.shape[1]-20,resized_img.shape[0]-100)
	cv2.grabCut(resized_img,mask,rect,bgdModel,fgdModel,30,cv2.GC_INIT_WITH_RECT)
	mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
	resized_img = resized_img*mask2[:,:,np.newaxis]
	print ("Image1...")
	mask1 = np.zeros(resized_img1.shape[:2],np.uint8)
	bgdModel1 = np.zeros((1,65),np.float64)
	fgdModel1 = np.zeros((1,65),np.float64)
	rect1 = (10,130,resized_img1.shape[1]-20,resized_img1.shape[0]-100)
	cv2.grabCut(resized_img1,mask1,rect1,bgdModel1,fgdModel1,30,cv2.GC_INIT_WITH_RECT)
	mask21 = np.where((mask1==2)|(mask1==0),0,1).astype('uint8')
	resized_img1 = resized_img1*mask21[:,:,np.newaxis]
	return (resized_img, resized_img1)

def hisComparision(img1, img2):
	print ("Histogram Comparision Started...")
	hist1 = cv2.calcHist([img1],[0],None,[256],[0,256])
	hist2 = cv2.calcHist([img2], [0], None, [256], [0,256])
	print ("Histogram Comparision Completed...")
	return cv2.compareHist(hist1,hist2,cv2.HISTCMP_CORREL)

def featureMatching(img1, img2):
	print ("Feature Matching Started...")

	img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
	img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

	orb = cv2.ORB_create()

	kp1, des1 = orb.detectAndCompute(img1, None)
	kp2, des2 = orb.detectAndCompute(img2, None)

	bf = cv2.BFMatcher(cv2.NORM_HAMMING2, crossCheck = True)

	matches = bf.match(des1, des2);
	matches = sorted(matches, key = lambda x:x.distance)

	img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:100], (0, 0, 255), flags = 2)
	cv2.imshow("img3", img3)
	cv2.waitKey(0)
	cv2.destroyAllWindows()
	print ("Feature Matching Completed...\nPrinting number of Matches...")
	return len(matches)

img1 = cv2.imread("C:\\Users\\hp\\Desktop\\MadeIT\\TEST\\P3.jpg")
img2 = cv2.imread("C:\\Users\\hp\\Desktop\\MadeIT\\TEST\\Z6.jpg")

#img1 = cv2.imread("C:\\Users\\Madhulika\\Documents\\GitHub\\OCR\\medicines\\P3.jpg")
#img2 = cv2.imread("C:\\Users\\Madhulika\\Documents\\GitHub\\OCR\\medicines\\Z5.jpg")
print ("Foreground Extraction Started...")
(img1, img2) = grabcut_func(img1, img2)
print("Foreground Extraction Complete...")

print (hisComparision(img1, img2))
print (featureMatching(img1, img2))


import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd.variable import Variable

device = 'cuda'
def weights_init(w):
    """
    Initializes the weights of the layer, w.
    """
    classname = w.__class__.__name__
    if classname.find('conv') != -1:
        nn.init.normal_(w.weight.data, 0.0, 0.02)
    elif classname.find('bn') != -1:
        nn.init.normal_(w.weight.data, 1.0, 0.02)
        nn.init.constant_(w.bias.data, 0)

# Define the Generator Network
class Generator(nn.Module):
    def __init__(self):
        super().__init__()
        self.n_c =3
        self.ngf = 32
        self.nz = 128
        # Input is the latent vector Z.
        self.tconv1 = nn.ConvTranspose2d(self.nz, self.ngf*16,
            kernel_size=4, stride=1, padding=0, bias=False)
        self.bn1 = nn.BatchNorm2d(self.ngf*16)

        # Input Dimension: (ngf*8) x 4 x 4
        self.tconv2 = nn.ConvTranspose2d(self.ngf*16, self.ngf*8,
            4, 2, 1, bias=False)
        self.bn2 = nn.BatchNorm2d(self.ngf*8)

        # Input Dimension: (ngf*4) x 8 x 8
        self.tconv3 = nn.ConvTranspose2d(self.ngf*8, self.ngf*4,
            4, 2, 1, bias=False)
        self.bn3 = nn.BatchNorm2d(self.ngf*4)

        # Input Dimension: (ngf*2) x 16 x 16
        self.tconv4 = nn.ConvTranspose2d(self.ngf*4, self.ngf*2,
            4, 2, 1, bias=False)
        self.bn4 = nn.BatchNorm2d(self.ngf*2)

        # Input Dimension: (ngf) * 32 * 32
        self.tconv5 = nn.ConvTranspose2d(self.ngf*2, self.ngf,
            4, 2, 1, bias=False)
        self.bn5 = nn.BatchNorm2d(self.ngf)

        self.tconv6 = nn.ConvTranspose2d(self.ngf, self.n_c,
            4, 2, 1, bias=False)
        #Output Dimension: (nc) x 64 x 64

    def forward(self, x):
        x = F.relu(self.bn1(self.tconv1(x)))
        x = F.relu(self.bn2(self.tconv2(x)))
        x = F.relu(self.bn3(self.tconv3(x)))
        x = F.relu(self.bn4(self.tconv4(x)))
        x = F.relu(self.bn5(self.tconv5(x)))

        x = F.tanh(self.tconv6(x))

        return x

# Define the Discriminator Network
class Discriminator(nn.Module):
    def __init__(self):
        super().__init__()
        self.n_c =3
        self.ndf = 12
        # Input Dimension: (nc) x 128 x 128
        self.conv1 = nn.Conv2d(self.n_c, self.ndf,
            4, 2, 1, bias=False)

        # Input Dimension: (ndf) x 64 x 64
        self.conv2 = nn.Conv2d(self.ndf, self.ndf*2,
            4, 2, 1, bias=False)
        self.bn2 = nn.BatchNorm2d(self.ndf*2)

        # Input Dimension: (ndf) x 32 x 32
        self.conv3 = nn.Conv2d(self.ndf*2, self.ndf*4,
            4, 2, 1, bias=False)
        self.bn3 = nn.BatchNorm2d(self.ndf*4)

        # Input Dimension: (ndf*2) x 16 x 16
        self.conv4 = nn.Conv2d(self.ndf*4, self.ndf*8,
            4, 2, 1, bias=False)
        self.bn4 = nn.BatchNorm2d(self.ndf*8)

        # Input Dimension: (ndf*4) x 8 x 8
        self.conv5 = nn.Conv2d(self.ndf*8, self.ndf*16,
            4, 2, 1, bias=False)
        self.bn5 = nn.BatchNorm2d(self.ndf*16)

        # Input Dimension: (ndf*8) x 4 x 4
        self.conv6 = nn.Conv2d(self.ndf*16, 1, 4, 1, 0, bias=False)

    def forward(self, x):
        x = F.leaky_relu(self.conv1(x), 0.2, True)
        x = F.leaky_relu(self.bn2(self.conv2(x)), 0.2, True)
        x = F.leaky_relu(self.bn3(self.conv3(x)), 0.2, True)
        x = F.leaky_relu(self.bn4(self.conv4(x)), 0.2, True)
        x = F.leaky_relu(self.bn5(self.conv5(x)), 0.2, True)

        x = F.sigmoid(self.conv6(x))

        return x

criterion = nn.BCELoss()

def noise(n, n_features=128):
    return Variable(torch.randn(n, n_features)).to(device)

def make_ones_4d(size):
    data = Variable(torch.ones(size, 1,1,1))
    return data.to(device)

def make_zeros_4d(size):
    data = Variable(torch.zeros(size, 1,1,1))
    return data.to(device)

def train_discriminator(optimizer, real_data, fake_data,dis):
    n = real_data.size(0)

    optimizer.zero_grad()

    prediction_real = dis(real_data)

    error_real = criterion(prediction_real, make_ones_4d(n))
    error_real.backward()

    prediction_fake = dis(fake_data)
    error_fake = criterion(prediction_fake, make_zeros_4d(n))

    error_fake.backward()
    optimizer.step()

    return error_real + error_fake

def train_generator(optimizer, fake_data,dis):
    n = fake_data.size(0)
    optimizer.zero_grad()

    prediction = dis(fake_data)
    error = criterion(prediction, make_ones_4d(n))

    error.backward()
    optimizer.step()

    return error


from setuptools import setup, find_packages

with open("./README.md") as f:
    long_description = f.read()

setup(
    name='sech3r',
    version='4.9',
    author="Captain Nick Lucifer",
    author_email="naryal2580@gmail.com",
    url="https://github.com/naryal2580/sech3r",
    download_url='https://github.com/naryal2580/sech3r/tarball/master',
    description="HTTP Security Header Checker",
    long_description=long_description,
    long_description_content_type="text/markdown",
    packages=find_packages(),
    py_modules=['sech3r'],
    install_requires=[
                        'stoyled',
                        'docopt'
                    ],
    classifiers=[
         "Programming Language :: Python :: 3",
         'Environment :: Console',
         "License :: OSI Approved :: MIT License",
         'Topic :: Security',
         "Operating System :: OS Independent",
     ],
    python_requires='>=3',
    entry_points={
        'console_scripts': [
            'sech3r = sech3r:run',
        ],
    }
 )


"""
View: For management info about items.
"""
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.urls import reverse_lazy, reverse
from django.views.generic import ListView, CreateView

from carts.forms import CartAddItemForm
from catalogue.forms import ItemBasicForm, ItemModelForm
from catalogue.models import ItemModel, CategoryModel

# GET - query param
@login_required(login_url="users:login")
def items_list(request):
    """

    """
    items = ItemModel.objects.filter(pvp__gt=5)  # Select * from item_model; -> [Queryset]
    # render(object request, <name_html>, context-> dict
    user = request.user  # None si no inicia sesion

    return render(request, "catalogue/index.html", {"products": items, "message": "Promociones"})

# POST - CREATE
def create_item(request):
    if request.method == "POST":
        form = ItemBasicForm(request.POST)
        if form.is_valid():  # Valida si los datos ingresados son correctos
            data = form.cleaned_data  # dict
            ItemModel.objects.create(**data)  # Crear el registro en bd
            messages.success(request, "Item creado exitosamente!")
            return HttpResponseRedirect(reverse("catalogue:item_list"))
        else:
            messages.error(request, "Data enviada no es válida!")
    form = ItemBasicForm()
    return render(request, "create.html", {"form": form})

# CRUD - Create , Read , Update , Delete
class ItemViewList(LoginRequiredMixin, ListView):
    """

    """
    model = ItemModel
    template_name = "catalogue/index.html"
    context_object_name = "products"
    redirect_field_name = reverse_lazy("users:login")
    queryset = ItemModel.objects.filter(pvp__gt=5)  # >

class ItemCreateView(CreateView):
    model = ItemModel
    form_class = ItemModelForm
    template_name = "create.html"
    success_url = reverse_lazy("catalogue:item_list")  # devuelve la url recibiendo el name.

# Catalogue
def item_catalogue(request, category_slug: str = None):
    category = None
    qs_categories = CategoryModel.objects.order_by("name")
    items = ItemModel.objects.order_by("name")
    if category_slug:
        category = get_object_or_404(CategoryModel, slug=category_slug)
        items = items.filter(category=category)  # []
    return render(request, "catalogue/list.html",
                  {"category": category, "categories": qs_categories, "items": items})

def item_detail(request, id: int, slug: str):
    item = get_object_or_404(ItemModel, id=id, slug=slug)
    cart_item_from = CartAddItemForm()
    return render(request, "catalogue/detail.html", {"item": item, "cart_item_form": cart_item_from})

# Search
def item_search(request):
    """

    """
    search_param = request.get("search")
    qs_items = ItemModel.objects.filter(
        Q(name__icontains=search_param) or Q(category__name__icontains=search_param)
    )
    return render(request, "catalogue/list.html", {"items": qs_items})