# Import necessary libraries
import joblib
import re
import streamlit as st
import numpy as np
import pandas as pd
import pprint
import warnings
import tempfile
from io import StringIO
from PIL import Image
from rake_nltk import Rake
import spacy
import spacy_streamlit
from collections import Counter

from nltk.tokenize import sent_tokenize
import tensorflow as tf
import en_core_web_sm

from rouge import Rouge
rouge = Rouge()

#custom imports
import text_summarize as ts
import text_classifier as tc
import text_analysis as nlp

# Warnings ignore
warnings.filterwarnings(action='ignore')
st.set_option('deprecation.showfileUploaderEncoding', False)
st.set_option('deprecation.showPyplotGlobalUse', False)

# Sidebar options
option = st.sidebar.selectbox('Navigation',
["Home",
	"Text Summarizer",
 "Text Classifier",
	"N-Gram Analysis",
	"Word Cloud",
	"Summarizer-Classifier"])

if option == 'Home':

	# Describing the Web Application

	# Title of the application
	#st.header("Researcher's Friendly Text Analytics Tool\n", )
	st.write(
			"""
				## Researcher's Friendly Text Analytics Tool
			"""
		)

	display = Image.open('display.jpg')
	display = np.array(display)
	st.image(display)
	#st.subheader("Shwetha, Sripradha, Supreetha, Tejaswini")
	#st.subheader("RNS Institute of Technology")
	st.write(
			"""
				## Project Description
				This is a text analytics webapp.
				The various text analytics methods like text summarizer, text classifier, N-gram analyser and a Word Cloud Generator are included.\n
				Text summarizer produces a tiny and crisp version of the corpus while preserving the important information which works based on textrank algorithm.\n
				Text classifier used to classify abstract sentences into the role they play (ie. Objective, Background, Methods, Results and Conclusion) to enable researchers to skim through the literature and dive deeper when necessary.\n
				N-gram analysis identifies all continuous adjacent sequences of words in a given sentence tokens. The N-gram plots can be used to analyse the relevance in the sequence of words.\n
				Word cloud generates the most frequent words in the texts and is a text visualization tool in which words are shown in varying sizes depending on how often they appear in the corpus. An additional feature is the image mask feature where the output can defined in a particular shape based on the image that is provided to the generator.\n
				These methods can be analysed on the corpus by selecting an option from the side bar.
			"""
		)

# Text Summarizer
elif option == "Text Summarizer":
	st.header("Text Summarization")
	#st.subheader("Enter the corpus that you want to summarize")
	#st.write("Text summarizer produces a shortened version of the corpus while preserving the important information. The total number of sentences in the shortened version can be chosen using the slider on the left side.")
	text_input = st.text_area("Enter the corpus that you want to summarize", height=200)
	ratio = st.sidebar.slider("Select number of sentences required in summary", min_value=0.0, max_value=1.0, value=0.1, step=0.1)
	sentence_count = len(sent_tokenize(text_input))
	ratio=int(ratio*sentence_count)
	st.write("Number of sentences:", sentence_count)

	if st.button("Summarize"):
		out = ts.summarize(text_input,ratio)
		st.write("**Summary Output:**", out)
		st.write("Number of output sentences:", len(sent_tokenize(out)))
		st.write("Rouge Score:",rouge.get_scores(text_input, out))

# Text Classify
elif option == "Text Classifier":
	st.header("Text Classification")
	#st.subheader("Enter an unstructured abstract that you want to classify")
	#st.write("Text classifier used to classify abstract sentences into the role they play (ie. Objective, Background, Methods, Results and Conclusion) to enable researchers to skim through the literature and dive deeper when necessary.")
	text_input = st.text_area("Enter an unstructured abstract that you want to classify", height=200)
	if st.button("Classify"):
		out = tc.classify(text_input)
		# st.write(out)

		st.write("**Classified Abstract:**")
		s=""
		for element in out:
			if element=='.':
				st.write(s)
				s=""
			else:
				s=s+element

# N-Gram Analysis Option
elif option == "N-Gram Analysis":

	st.header("N-Gram Analysis")
	#st.subheader("This feature displays the most commonly occuring N-Grams in the entered text")
	#st.write("N-gram is a contiguous sequence of n items from a given sample of text or speech. The items can be phonemes, syllables, letters, words or base pairs according to the application. The n-grams typically are collected from a text or speech corpus. This tool identifies the most commonly occurring n-grams. ")
	# Ask for text or text file
	#st.subheader('Enter text below')
	text = st.text_area('Enter a paragraph', height=200)

	# Parameters
	n = st.sidebar.slider("N for the N-gram", min_value=1, max_value=8, step=1, value=2)
	topk = st.sidebar.slider("Top k most common phrases", min_value=10, max_value=50, step=5, value=10)

	# Add a button
	if st.button("Generate N-Gram Plot"):
		# Plot the ngrams
		nlp.plot_ngrams(text, n=n, topk=topk)
		st.pyplot()

# Word Cloud Feature
elif option == "Word Cloud":

	st.header("Word Cloud")
	#st.subheader("This feature generates a word cloud that contains the most frequent words in the entered text")
	#st.write("A word cloud is a data visualization method that forms an integral aspect of text analytics. It is a text representation in which words are shown in varying sizes depending on how often they appear in the corpus. The words with higher frequency are given a bigger font and stand out from the words with lower frequencies. An additional feature is the image mask feature where the output can defined in a particular shape based on the image that is provided to the generator.")
	# Ask for text or text file
	#st.header('Enter text')
	text = st.text_area('Enter a paragraph', height=200)

	# Upload mask image
	mask = st.file_uploader('Use Image Mask', type = ['jpg'])

	if st.button("Generate Wordcloud"):
		st.write(len(text))
		nlp.create_wordcloud(text, mask)
		st.pyplot()

elif option == "Summarizer-Classifier":
	st.header("Summarizer-Classifier")
	#st.write("This is an integration of text summarizer and classifier. The text summarizer first produces a shortened version of the given corpus. The number of sentences in the shortened version can be chosen as per requirements. The output of the summarizer is fed to the classifier which classifies the sentences in the summarized text into the role they play (ie. Objective, Background, Methods, Results and Conclusion)")
	#st.subheader("Enter the corpus that you want to summarize")
	text_input = st.text_area("Enter a corpus that you want to summarize and classify", height=200)
	ratio = st.sidebar.slider("Select number of sentences required in summary", min_value=0.0, max_value=1.0, value=0.1, step=0.1)
	sentence_count = len(sent_tokenize(text_input))
	ratio=int(ratio*sentence_count)
	st.write("Number of sentences:", sentence_count)

	if st.button("Summarize and Classify"):
		int_out = ts.summarize(text_input,ratio)
		fin_out=tc.classify(int_out)
		st.write("**Summary Output:**", int_out)
		st.write("Number of output sentences:", len(sent_tokenize(int_out)))

		st.write("**Classified Abstract:**")
		s=""
		for element in fin_out:
			if element=='.':
				st.write(s)
				s=""
			else:
				s=s+element


from __future__ import absolute_import, division

import math
import re
import logging

NaN = float('nan')

expr_re = re.compile(r"""
    (\w+)  # Function name
    \(
    ([^)]+) # Yup, you can put anything in the parens
    \)
""", re.VERBOSE | re.IGNORECASE)

def parse_expr(expr):
    fx_map = {
        'sum': ag_sum,
        'sum_imputed': ag_sum_imputed,
        'mean': ag_mean,
        'mean_imputed': ag_mean_imputed,
        'imputed_fraction': ag_imputed_fraction,
        'join': ag_join,
        'ratio': ag_ratio,
        'max': ag_max,
        'min': ag_min,
    }
    try:
        fx_name, measure_names = expr_re.match(expr).groups()
        fx_name = fx_name.lower()
        measure_names = [m.strip() for m in measure_names.split(",")]
    except AttributeError as err:
        raise AggregatorError(
            "I don't understand {0!r}: {1}".format(expr, err))
    try:
        fx = fx_map[fx_name]
    except KeyError:
        raise AggregatorError("I don't know {0}".format(fx_name))
    return (fx_name, fx, measure_names)

def isfinite(val):
    try:
        f_val = float(val)
    except (TypeError, ValueError):
        return False
    return not (math.isnan(f_val) or math.isinf(f_val))

def to_f(val):
    try:
        return float(val)
    except (TypeError, ValueError):
        return None

def float_or_imputed(value, imputed_value):
    if isfinite(value):
        return to_f(value)
    return imputed_value

def numeric_only(values):
    floated_vals = [to_f(val) for val in values]
    return [val for val in floated_vals if isfinite(val)]

def impute_mean(values):
    numeric_values = numeric_only(values)
    mean_val = ag_mean(numeric_values)
    return [float_or_imputed(val, mean_val) for val in values]

# sum is a reserved word in python because goddammt
def ag_sum(values):
    return math.fsum([float(v) for v in values])

def ag_mean(values):
    return ag_sum(values)/(len(values))

def ag_mean_imputed(values):
    try:
        with_imputed = impute_mean(values)
        mean_val = ag_mean(with_imputed)
        logging.debug("mean({}): {}".format(with_imputed, mean_val))
        return mean_val
    except ZeroDivisionError:
        return NaN

def ag_sum_imputed(values):
    try:
        with_imputed = impute_mean(values)
        sum_val = ag_sum(with_imputed)
        logging.debug("sum({}): {}".format(with_imputed, sum_val))
        return sum_val
    except ZeroDivisionError:
        return NaN

def ag_imputed_fraction(values):
    """ The fraction of non-float-able items in values """
    imputed_count = len(values) - len(numeric_only(values))
    return imputed_count / len(values)

def ag_join(values):
    # Filters out empty values.
    values = [str(v) for v in values if len(str(v).strip()) > 0]
    return '|'.join([str(v) for v in values])

def ag_ratio(values):
    if len(values) != 2:
        raise AggregatorError("ratio() needs two values")
    try:
        return float(values[0] / float(values[1]))
    except ZeroDivisionError:
        return NaN

def ag_max(values):
    return max([float(v) for v in values])

def ag_min(values):
    return min([float(v) for v in values])

class AggregatorError(ValueError):
    pass


"""empty message

Revision ID: b387260525c7
Revises:
Create Date: 2021-09-30 11:37:00.865023

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = 'b387260525c7'
down_revision = None
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('users',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('public_id', sa.String(length=50), nullable=True),
    sa.Column('username', sa.String(length=255), nullable=False),
    sa.Column('email', sa.String(length=255), nullable=False),
    sa.Column('password', sa.String(length=255), nullable=False),
    sa.Column('bio', sa.String(length=255), nullable=True),
    sa.Column('admin', sa.Boolean(), nullable=True),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('email'),
    sa.UniqueConstraint('public_id'),
    sa.UniqueConstraint('username')
    )
    op.create_table('libraries',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('title', sa.String(length=255), nullable=False),
    sa.Column('name', sa.Text(), nullable=False),
    sa.Column('user_id', sa.Integer(), nullable=True),
    sa.Column('book_pic', sa.String(length=255), nullable=False),
    sa.Column('lend', sa.Boolean(), nullable=True),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('lender',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('library_id', sa.Integer(), nullable=True),
    sa.Column('user_id', sa.Integer(), nullable=True),
    sa.Column('price', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['library_id'], ['libraries.id'], ),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('reviews',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('review', sa.Text(), nullable=False),
    sa.Column('user_id', sa.Integer(), nullable=False),
    sa.Column('library_id', sa.Integer(), nullable=False),
    sa.ForeignKeyConstraint(['library_id'], ['libraries.id'], ),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('reviews')
    op.drop_table('lender')
    op.drop_table('libraries')
    op.drop_table('users')
    # ### end Alembic commands ###


#!/usr/bin/env python
# -*- coding: UTF8 -*-

# Author: Guillaume Bouvier -- guillaume.bouvier@pasteur.fr
# https://research.pasteur.fr/en/member/guillaume-bouvier/
# 2017-02-20 15:50:56 (UTC+0100)

import numpy
import scipy.spatial.distance
import re
from EMEC import em_fit
from EMEC import optimizer
import sys

sys.path.append('/home/bougui/source/modeller_repair_protein')
import repair_protein
from EMEC import Graph

def read_pdb(pdbfilename):
    """
    Read the given PDB filename and returns the atom_types, aa_list and a
    numpy array with the coordinates
    """
    splitted_lines = []
    with open(pdbfilename) as pdbfile:
        for line in pdbfile:
            if line[:4] == 'ATOM':
                splitted_line = [
                    line[:6], line[6:11], line[12:16], line[17:20], line[21], line[22:26], line[30:38], line[38:46],
                    line[46:54]
                ]
                splitted_lines.append(splitted_line)
    splitted_lines = numpy.asarray(splitted_lines)
    atom_types = [e.strip() for e in splitted_lines[:, 2]]
    aa_list = [e.strip() for e in splitted_lines[:, 3]]
    chain_ids = [e.strip() for e in splitted_lines[:, 4]]
    resids = [int(e) for e in splitted_lines[:, 5]]
    # Check if the given pdbfile is a CA trace only
    is_ca_trace = len(set(resids)) == len(resids)
    return atom_types, aa_list, chain_ids, resids, numpy.float_(splitted_lines[:, 6:9]), is_ca_trace

def get_cmap(pdbfile, threshold=5.):
    """
    Contact map for the all atom protein.
    The pairwise distance between all atoms of each residue is computed and
    the minimal distance between atoms of residue i and residue j is kept.
    • pdbfile: file name for the all atoms PDB file
    • threshold: distance threshold above which defined a contact
    """
    atom_types, aa_list, chain_ids, resids, coords, _ = read_pdb(pdbfile)
    pdist = scipy.spatial.distance.squareform(scipy.spatial.distance.pdist(coords))
    cmap = numpy.zeros((len(set(resids)), ) * 2)
    for pt_i, i in enumerate(numpy.unique(resids)):
        for pt_j, j in enumerate(numpy.unique(resids)):
            cmap[pt_i, pt_j] = pdist[resids == i][:, resids == j].min()
    cmap = numpy.int_(cmap <= threshold)
    return cmap

class AllAtoms(object):
    """
    Build an all atom protein fitting the EM density from a CA trace
    """
    def __init__(self, pdbfile, fasta, emd, level, mrc, resolution, alpha=None, strand=None, basename=None):
        """
        • pdbfile: coordinates for the CA trace in PDB format
        • fasta: fasta file with the sequence aligned using map_align
        • emd: EM density map file in nc format
        • level: threshold to apply to the EM density map
        • mrc: EM density file in MRC format for modeller
        • resolution: resolution of the EM map for modeller
        • alpha: residue slices with alpha helix restraints
        • strand: residue slices with beta strand restraints
        • basename: basename of the output modeller files
                    (default: modeller_out)
        """
        self.mrc = mrc
        self.resolution = resolution

        if basename is None:
            self.basename = "modeller_out"
        else:
            self.basename = basename

        self.fasta = fasta
        self.pdbfile = pdbfile
        self.alpha = alpha
        self.strand = strand
        atom_types, aa_list, chain_ids, resids, coords, is_ca_trace = read_pdb(pdbfile)
        if is_ca_trace:
            self.atom_types, self.aa_list, self.chain_ids, self.resids, self.coords, _ = self.ca_to_all()
            self.cmap = get_cmap('%s.pdb' % self.basename)  # Contact map for the all atoms protein
            self.n_atoms = self.coords.shape[0]
            self.topology = self.get_topology()  # topology (adjacency matrix) for
            # the all atom protein
        else:
            # already all atom
            self.atom_types, self.aa_list, self.chain_ids, self.resids, self.coords = \
            atom_types, aa_list, chain_ids, resids, coords
            self.cmap = get_cmap(pdbfile)
        self.emd = emd
        self.level = level

    def ca_to_all(self):
        """
        Convert the CA trace to all atoms
        """
        repair_protein.repair_protein(self.pdbfile,
                                      sequence=self.fasta,
                                      write_psf=True,
                                      emd=self.mrc,
                                      resolution=self.resolution,
                                      alpha=self.alpha,
                                      strand=self.strand,
                                      outpdb="%s.pdb" % self.basename)
        # return the coordinates of the all atoms protein
        return read_pdb('%s.pdb' % self.basename)

    @property
    def ca_trace(self):
        """
        Coordinates of the CA trace
        """
        selection = numpy.asarray(self.atom_types) == 'CA'
        return self.coords[selection]

    def get_topology(self):
        """
        Read the BONDS section of the PSF to build the corresponding adjacency
        matrix
        """
        psffilename = '%s.psf' % self.basename
        start = False
        topology = numpy.ones((self.n_atoms, self.n_atoms)) * numpy.inf
        with open(psffilename) as psffile:
            for line in psffile:
                if re.findall('!NTHETA', line):
                    start = False
                if start:
                    bonds = numpy.int_(line.split()) - 1
                    for i, j in zip(bonds, bonds[1:]):
                        topology[i, j] = 1
                        topology[j, i] = 1
                if re.findall('!NBOND', line):
                    start = True
        return topology

    def fit(self):
        """
        Fit the all atom structures onto the EM density map
        """
        graph = Graph.Graph(self.topology)
        graph.minimum_spanning_tree = self.topology
        emf = em_fit.Fit(self.emd,
                         self.level,
                         graph,
                         self.coords,
                         20000,
                         alpha_0=.1,
                         alpha_1=0.0,
                         radius_0=1.,
                         radius_1=0.,
                         refine=True)
        emf.learn()
        self.write_pdb(pdbname="%s.pdb" % self.basename)
        repair_protein.repair_protein('%s.pdb' % self.basename,
                                      emd=self.mrc,
                                      resolution=self.resolution,
                                      outpdb=self.basename)
        repair_protein.align_structures(self.pdbfile, '%s.pdb' % self.basename)
        self.atom_types, self.aa_list, self.chain_ids, self.resids, self.coords, _ = read_pdb('%s.pdb' % self.basename)

    def write_pdb(self, pdbname='modeller_out.pdb'):
        """
        Write a PDB file to disk
        """
        connect = numpy.asarray(numpy.where(~numpy.isinf(self.topology))).T + 1
        optimizer.write_pdb(self.coords,
                            sequence=self.aa_list,
                            atoms=self.atom_types,
                            chain_ids=self.chain_ids,
                            resids=self.resids,
                            outfilename=pdbname,
                            write_conect=False)


import habitat_sim
from habitat_sim.utils.common import d3_40_colors_rgb, quat_to_angle_axis, quat_to_coeffs, quat_from_coeffs, quat_from_angle_axis
from habitat.utils.visualizations import maps
import magnum as mn
import numpy as np
import math
import matplotlib.pyplot as plt
from PIL import Image
import random
from skimage.color import rgb2gray
import habitat_sim.errors

""" Notes:
        1. It is weird that the Matterport3D and Gibson does not come with the semantic offline_dataset.
        2. The .navmesh file in the same folder will be loaded simultaneously with the .ply or .glb file.
        3. The environment provides single RGB, RGB-D, Depth, and Semantic observations.
        4. The environment provides panoramic RGB, RGB-D, Depth, and Semantic observations.
        5. The environment provides top down observation (agent).
        6. The agent is randomly spawn. The location is randomly chosen from a continuous space and orientation
           is randomly chosen from [east, north, west, south]
        7. The output depth can be clipped to a fixed range see depth_clip_vmax
        8. The back observation is not flipped anymore. (changed in NeurIPs 22 release)
"""

class House(object):
    def __init__(self,
                 scene=None,
                 enable_rgb=False,
                 enable_depth=False,
                 enable_rgb_depth=False,
                 enable_semantic=False,
                 enable_panorama=False,
                 enable_obs_noise=False,
                 enable_act_noise=False,
                 enable_local_map=False,
                 top_down_type="binary",
                 sensor_height=1.5,
                 rnd_seed=1234,
                 map_meters_per_pixel=0.01,
                 local_map_size=50,
                 enable_ego_local_map=False,
                 obs_width=224,
                 obs_height=224,
                 move_forward_amount=0.25,
                 turn_left_amount=10,
                 turn_right_amount=10,
                 map_show_agent=False,
                 map_show_goal=False,
                 goal_reach_eps=0.2,  # from active neural slam paper
                 depth_clip_vmax=False,
                 allow_sliding=False,
                 max_episode_length=500,
                 plot_trajectories=False
                 ):
        """
        The wrapper class for Facebook Habitat environment with several customized functions for PointGoal navigation
        :param scene: the absolute path of the scene files. For example, name.glb (Gibson); name.ply (Replica); string
        :param enable_rgb: if True, enable the RGB observations; bool
        :param enable_depth: if True, enable the Depth observations; bool
        :param enable_rgb_depth: if True, enable the RGB-D observations; bool
        :param enable_semantic: if True, enable the Semantic observations; bool
        :param enable_panorama: if True, enable the panoramic observations; bool
        :param enable_obs_noise: if True, enable the noisy observations; bool
        :param enable_act_noise: if True, enable the noisy actions; bool
        :param enable_local_map: if True, the local map is plotted; Otherwise,the global map is plotted; bool
        :param enable_physics: related to kinematics; by default: False; bool
        :param top_down_type: binary for binary occupancy map; boundary for RGB occupancy map; string
        :param sensor_height: camera height (m); float
        :param rnd_seed: random seed; by default: 1234; int
        :param map_meters_per_pixel: map roughness parameter, number of meters for one pixel; by default 0.01; float
        :param local_map_size: 1/2 size of the local map; by default 50; int
        :param obs_width: width of the observation; @param {type: "integer"}
        :param obs_height: height of the observation; int
        :param move_forward_amount: number of meters for one forward action (m); float
        :param turn_left_amount: number of degrees for the turning left action (degree); float
        :param turn_right_amount: number of degrees for the turning right action (degree); float
        :param map_show_agent: if True, show agent on the map; bool
        :param map_show_goal: if True, show goal on the map; bool
        :param goal_reach_eps: termination signal, if distance (agent, goal) <= eps, agents reach the goal; float
        :param enable_ego_local_map: if True, plot the map in an egocentric way; bool
        :param depth_clip_vmax: max value to clip the depth observation; float
        :param allow_sliding: if False, the sliding behavior is disabled; @param {type: "boolean"}
        """

        # check the scene validation
        assert scene is not None, f"Invalid scene error: Got {scene}. Please check the path/name of the scene file."

        # scene file in: .ply or .glb
        self.scene = scene

        # epsilon for goal reaching
        self.dist_eps = goal_reach_eps

        # set randomness
        self.rnd_seed = rnd_seed

        """
            Sensor configurations
        """
        self.enable_rgb = enable_rgb
        self.enable_depth = enable_depth
        self.enable_rgb_depth = enable_rgb_depth
        self.enable_semantic = enable_semantic
        self.enable_panorama = enable_panorama
        self.enable_noise_obs = enable_obs_noise
        self.depth_clip_vmax = depth_clip_vmax
        panorama_names = ["_front", "_left", "_right", "_back"]
        # create the list containing sensor names
        # for monocular: [color_sensor, depth_sensor]
        # for panoramic: [color_sensor_front, color_sensor_left, color_sensor_right, color_sensor_back]
        # note that: rgb-d observations contain both color_sensors and depth_sensors
        if self.enable_panorama:
            if self.enable_rgb_depth:
                sensor_names = ['color_sensor' + item for item in panorama_names] + \
                               ['depth_sensor' + item for item in panorama_names]
            elif self.enable_rgb:
                sensor_names = ['color_sensor' + item for item in panorama_names]
            elif self.enable_depth:
                sensor_names = ['depth_sensor' + item for item in panorama_names]
            elif self.enable_semantic:
                sensor_names = ['semantic_sensor' + item for item in panorama_names]
            else:
                raise Exception("Invalid observation")
        else:
            if self.enable_rgb:
                sensor_names = ["color_sensor"]
            elif self.enable_depth:
                sensor_names = ["depth_sensor"]
            elif self.enable_rgb_depth:
                sensor_names = ["color_sensor", "depth_sensor"]
            else:
                raise Exception("Invalid observation")

        # set the sensor names
        self.sensor_names = sensor_names
        # add top-down observation
        self.sensor_names.append("top_down")

        """
            Habitat settings
        """
        self.habitat_settings = {
            "scene": scene,  # path the scene
            "default_agent": 0,  # index of the default agent
            "sensor_height": sensor_height,  # height of sensor in meters
            "width": obs_width,  # observation image width
            "height": obs_height,  # observation image height
            "color_sensor": enable_rgb,  # use RGB sensor
            "depth_sensor": enable_depth,  # use Depth sensor
            "semantic_sensor": enable_semantic,  # use semantic sensor
            "seed": rnd_seed,  # used in random navigation
            "enable_physics": False  # for kinematic only
        }

        # define the action space
        # todo: The action space could be customized (https://aihabitat.org/docs/habitat-sim/new-actions.html)
        self.enable_act_noise = enable_act_noise
        self.action_space = ['move_forward', 'turn_left', 'turn_right']
        self.move_forward_amount = move_forward_amount
        self.turn_left_amount = turn_left_amount
        self.turn_right_amount = turn_right_amount

        # simulator initial configurations
        self.sim_cfg = None
        # agent initial configurations
        self.agent_cfg = None
        # control the sliding behavior
        self.allow_sliding = allow_sliding
        # create simulator, agent, and optimal path follower
        self.simulator, self.agent, self.follower = self.create_simulator_and_agent()

        """
            Map configurations
        """
        self.enable_local_map = enable_local_map
        self.meters_per_pixel = map_meters_per_pixel
        self.local_map_size = local_map_size
        self.map_show_agent = map_show_agent
        self.map_show_goal = map_show_goal
        self.enable_ego_local_map = enable_ego_local_map
        if top_down_type == "binary":
            self.top_down_map = self.render_top_down_view_binary()  # occupancy map: binary representation
        elif top_down_type == "boundary":
            self.top_down_map = self.render_top_down_view_habitat()  # occupancy map: RGB representation
            self.top_down_gray_map = self.rgb2gray(self.top_down_map.copy())  # convert to gray
        else:
            raise Exception("Invalid top down map mode.")
        # plot the trajectories on the map
        self.top_down_map_traj = self.top_down_map.copy()

        """
            Environment parameters
        """
        boundary = self.simulator.pathfinder.get_bounds()  # Environment parameters
        self.min_x = boundary[0][0]
        self.max_x = boundary[1][0]  # height?
        self.min_y = boundary[0][2]
        self.max_y = boundary[1][2]  # width?

        """
            Plot the figures
        """
        self.fig = None
        self.arr = None
        self.artists = []
        self.plot_traj = plot_trajectories

        """
            Episode parameters
        """
        # sample the start location, the goal location, and the geometric distance
        self.start_loc, self.goal_loc, self.geo_dist = self.sample_start_goal_locations()
        # record the shortest path
        self.shortest_path = None
        # track the geometric distance between the agent and the goal locations
        self.agent_goal_dist = np.inf
        # maximal episode length
        self.max_episode_length = max_episode_length
        # time step counter
        self.step_counter = 0

    # render binary top down observation
    def render_top_down_view_binary(self):
        height = self.simulator.pathfinder.get_bounds()[0][1]
        sim_top_down_map = self.simulator.pathfinder.get_topdown_view(self.meters_per_pixel,
                                                                      height)
        return sim_top_down_map

    # render boundary top down observation
    def render_top_down_view_habitat(self):
        height = self.simulator.pathfinder.get_bounds()[0][1]
        top_down_map = maps.get_topdown_map(
            self.simulator.pathfinder, height, meters_per_pixel=self.meters_per_pixel
        )
        recolor_map = np.array(
            [[255, 255, 255], [128, 128, 128], [0, 0, 0]], dtype=np.uint8
        )
        top_down_map = recolor_map[top_down_map]
        return top_down_map

    # sensor specifications
    def embedded_agent_sensor_specs(self):
        # sensors
        sensor_specs = []

        # default observation resolution
        img_height = self.habitat_settings['height']
        img_width = self.habitat_settings['width']
        sensor_height = self.habitat_settings['sensor_height']

        # function to get specifications
        def obtain_sensor_spec(sensor_type, sensor_uuid, orientation):
            # get standard sensor specification
            _spec = habitat_sim.CameraSensorSpec()
            # set sensor uuid (i.e., indicator)
            _spec.uuid = sensor_uuid
            # set sensor type
            if sensor_type == "color":
                _spec.sensor_type = habitat_sim.SensorType.COLOR
            elif sensor_type == "depth":
                _spec.sensor_type = habitat_sim.SensorType.DEPTH
            elif sensor_type == "semantic":
                _spec.sensor_type = habitat_sim.SensorType.SEMANTIC
            else:
                raise Exception(f"Invalid sensor type. Expect color, depth, or semantic, but get {sensor_type}")
            # set sensor model
            _spec.sensor_subtype = habitat_sim.SensorSubType.PINHOLE
            # set sensor resolution, position and orientation
            _spec.resolution = [img_height, img_width]
            _spec.position = [0.0, sensor_height, 0.0]  # [x, y, z], meters
            _spec.orientation = orientation  # [pitch, yaw, roll], radius, counter-clock wise

            # enable observation noise
            if self.enable_noise_obs:
                if sensor_type == "color":
                    _spec.noise_model = "GaussianNoiseModel"
                    _spec.noise_model_kwargs = dict(intensity_constant=0.1)
                elif sensor_type == "depth":
                    _spec.noise_model = "RedwoodDepthNoiseModel"
                    _spec.noise_model_kwargs = dict(noise_multiplier=1.0)
                else:
                    raise Exception("Wrong sensor type")
            return _spec

        if not self.enable_panorama:
            # set the RGB camera
            if self.enable_rgb:
                # get rgb camera
                rgb_sensor_spec = obtain_sensor_spec("color", "color_sensor", [0.0, 0.0, 0.0])
                # add the rgb camera
                sensor_specs.append(rgb_sensor_spec)

            # set the depth camera
            if self.enable_depth:
                # get depth camera
                depth_sensor_spec = obtain_sensor_spec("depth", 'depth_sensor', [0.0, 0.0, 0.0])
                # add the depth camera
                sensor_specs.append(depth_sensor_spec)

            # set the semantic camera
            if self.enable_semantic:
                # get semantic camera
                semantic_sensor_spec = obtain_sensor_spec('semantic', 'semantic_sensor', [0.0, 0.0, 0.0])
                # add the semantic sensor
                sensor_specs.append(semantic_sensor_spec)

            # set the RGB-D camera
            if self.enable_rgb_depth:
                # get the RGB camera
                rgb_sensor_spec = obtain_sensor_spec('color', 'color_sensor', [0.0, 0.0, 0.0])
                # get the depth camera
                depth_sensor_spec = obtain_sensor_spec('depth', 'depth_sensor', [0.0, 0.0, 0.0])
                # add the RGB and depth cameras
                sensor_specs.append(rgb_sensor_spec)
                sensor_specs.append(depth_sensor_spec)
        else:
            spec_orientations = [[0.0, 0.0, 0.0], [0.0, np.pi, 0.0], [0.0, np.pi / 2, 0.0], [0.0, -1 * np.pi / 2, 0.0]]
            spec_uuids = ["sensor_front", "sensor_back", "sensor_left", "sensor_right"]
            if self.enable_rgb:
                for spec_ori, spec_uuid in zip(spec_orientations, spec_uuids):
                    sensor_specs.append(obtain_sensor_spec("color", "color_" + spec_uuid, spec_ori))

            if self.enable_depth:
                for spec_ori, spec_uuid in zip(spec_orientations, spec_uuids):
                    sensor_specs.append(obtain_sensor_spec("depth", "depth_" + spec_uuid, spec_ori))

            if self.enable_semantic:
                for spec_ori, spec_uuid in zip(spec_orientations, spec_uuids):
                    sensor_specs.append(obtain_sensor_spec("semantic", "semantic_" + spec_uuid, spec_ori))

            if self.enable_rgb_depth:
                for spec_ori, spec_uuid in zip(spec_orientations, spec_uuids):
                    sensor_specs.append(obtain_sensor_spec("color", "color_" + spec_uuid, spec_ori))
                    sensor_specs.append(obtain_sensor_spec("depth", "depth_" + spec_uuid, spec_ori))

        # set the sensor specifications
        self.agent_cfg.sensor_specifications = sensor_specs

    def embedded_agent_action_specs(self):
        if self.enable_act_noise:
            self.agent_cfg.action_space = dict(
                move_forward=habitat_sim.ActionSpec(
                    "move_forward",
                    habitat_sim.PyRobotNoisyActuationSpec(
                        amount=self.move_forward_amount,
                        robot="LoCoBot",
                        controller="Proportional",
                        noise_multiplier=1.0
                    ),
                ),
                turn_left=habitat_sim.ActionSpec(
                    "turn_left",
                    habitat_sim.PyRobotNoisyActuationSpec(
                        amount=self.turn_left_amount,
                        robot="LoCoBot",
                        controller="Proportional",
                        noise_multiplier=0.5
                    ),
                ),
                turn_right=habitat_sim.ActionSpec(
                    "turn_right",
                    habitat_sim.PyRobotNoisyActuationSpec(
                        amount=self.turn_right_amount,
                        robot="LoCoBot",
                        controller="Proportional",
                        noise_multiplier=0.5
                    ),
                )
            )
        else:
            self.agent_cfg.action_space = {
                "move_forward": habitat_sim.agent.ActionSpec(
                    "move_forward", habitat_sim.agent.ActuationSpec(amount=self.move_forward_amount)
                ),
                "turn_left": habitat_sim.agent.ActionSpec(
                    "turn_left", habitat_sim.agent.ActuationSpec(amount=self.turn_left_amount)
                ),
                "turn_right": habitat_sim.agent.ActionSpec(
                    "turn_right", habitat_sim.agent.ActuationSpec(amount=self.turn_right_amount)
                ),
            }

    # create the simulator and agent
    def create_simulator_and_agent(self):
        # create the simulator configurations
        self.sim_cfg = habitat_sim.SimulatorConfiguration()  # get the configuration object for the simulator
        self.sim_cfg.gpu_device_id = 0  # assign a GPU to the simulator
        self.sim_cfg.scene_id = self.habitat_settings["scene"]  # define the scene name with the ply files
        self.sim_cfg.enable_physics = self.habitat_settings['enable_physics']  # enable the physics

        # create the agent configurations
        self.agent_cfg = habitat_sim.agent.AgentConfiguration()  # get the configuration object for the agent

        # create the sensor configurations
        self.embedded_agent_sensor_specs()

        # create the action configurations
        self.embedded_agent_action_specs()

        # create the simulator
        self.sim_cfg = habitat_sim.Configuration(self.sim_cfg, [self.agent_cfg])
        simulator = habitat_sim.Simulator(self.sim_cfg)

        # disable the sliding behavior
        simulator.config.sim_cfg.allow_sliding = self.allow_sliding

        # create the agent
        agent = simulator.initialize_agent(0)
        # set the agent state
        agent_state = habitat_sim.AgentState()
        agent_state.position = np.array([0.0, 0.0, 0.0])  # the agent is set at [0, 0, 0] by default
        agent.set_state(agent_state)

        # create follower for optimal path planning
        follower = habitat_sim.nav.GreedyGeodesicFollower(
            pathfinder=simulator.pathfinder,
            agent=agent,
            goal_radius=self.dist_eps,
            forward_key="move_forward",
            left_key="turn_left",
            right_key="turn_right"
        )

        return simulator, agent, follower

    def is_on_map(self, loc):
        c = int((loc[0] - self.min_x) // self.meters_per_pixel)
        r = int((loc[2] - self.min_y) // self.meters_per_pixel)

        return not self.top_down_gray_map[r, c] == 1.0

    # sample navigable start and goal locations
    def sample_start_goal_locations(self):
        while True:
            # sample start and goal locations
            s_loc = self.simulator.pathfinder.get_random_navigable_point()
            g_loc = self.simulator.pathfinder.get_random_navigable_point()

            # exam whether the path exists
            path = habitat_sim.ShortestPath()
            path.requested_start = s_loc
            path.requested_end = g_loc
            found_path = self.simulator.pathfinder.find_path(path)
            geodesic_distance = path.geodesic_distance
            if found_path is True and self.is_on_map(s_loc) and self.is_on_map(g_loc):
                # return start, goal locations and geometry distance
                return s_loc, g_loc, geodesic_distance

    # set fixed start and goals
    def set_start_goal_locations(self):
        path = habitat_sim.ShortestPath()
        path.requested_start = self.start_loc
        path.requested_end = self.goal_loc
        self.simulator.pathfinder.find_path(path)
        geodesic_distance = path.geodesic_distance
        return geodesic_distance

    # compute the geometric ditance
    def compute_geo_distance(self, start_loc, goal_loc):
        path = habitat_sim.ShortestPath()
        path.requested_start = start_loc
        path.requested_end = goal_loc
        self.simulator.pathfinder.find_path(path)
        return path.geodesic_distance

    # set agent state
    def set_agent_state(self, loc=None, ori=None):
        agent_state = habitat_sim.AgentState()
        agent_state.position = self.start_loc if loc is None else loc
        ori = quat_from_angle_axis(ori, np.array([0, 1, 0]))  # pitch, yaw, roll
        agent_state.rotation = agent_state.rotation if ori is None else ori
        self.agent.set_state(agent_state)

    # render agent observations
    def render_agent_observations(self):
        # render from the simulator
        observations = self.simulator.get_sensor_observations()

        # save the observation in a dict
        return_obs = {}
        for name in self.sensor_names:
            if name == "top_down":  # render top down observation
                return_obs[name] = self.draw_agent_on_top_down_map()
            else:  # render sensor observations
                if "depth" in name and self.depth_clip_vmax:  # clip the depth image if depth_clip_vmax != 0
                    observations[name] = np.clip(observations[name], a_min=0, a_max=self.depth_clip_vmax)

                return_obs[name] = observations[name]

        return return_obs

    # render agent state
    def render_agent_state(self):
        return self.agent.state.position.tolist() + quat_to_coeffs(self.agent.state.rotation).tolist()

    # gym reset function
    def reset(self, start_loc=None, goal_loc=None):
        # reset the step counter
        self.step_counter = 0

        # randomly sample the start ang goal locations
        if start_loc is None or goal_loc is None:
            # randomly sample the start and goal locations
            self.start_loc, self.goal_loc, self.geo_dist = self.sample_start_goal_locations()

            # # todo: debug
            # self.start_loc = np.array([-0.15,  0.17,  1.55])
            # self.goal_loc = np.array([-3.67,  0.17, 0.83])

            # reset the top-down map
            self.top_down_map_traj = self.top_down_map.copy()
        else:
            # set the start and goal locations
            self.start_loc = np.array(start_loc).astype(np.float32)
            self.goal_loc = np.array(goal_loc).astype(np.float32)
            self.set_start_goal_locations()
            # query the shortest path
            self.query_shortest_path(self.start_loc, self.goal_loc)
            # draw the trajectory on the top-down map
            self.top_down_map_traj = self.display_shortest_path()

        # randomly sample the agent's orientation [0, np.pi / 2, np.pi, np.pi * 3 / 2]
        ori = random.sample([0, np.pi/2, np.pi, np.pi * 3 / 2], 1)[0]
        # set the agent's orientation
        self.set_agent_state(ori=ori)

        # render agent observation
        obs = self.render_agent_observations()

        # render agent state e.g. [x, y, z, rotation]
        obs['state'] = self.render_agent_state()
        return obs

    # gym step function
    def step(self, act):
        # step
        self.simulator.step(act)
        # render observation
        obs = self.render_agent_observations()
        # render state
        obs['state'] = self.render_agent_state()
        # increase the time step
        self.step_counter += 1
        # check the termination
        done = self.is_terminal(obs['state'])

        return obs, done

    # gym render function
    def render(self, observation, time_step):
        if self.enable_panorama:
            if self.enable_rgb:
                self.display_panorama(observation, time_step, "color_sensor")

            if self.enable_depth:
                self.display_panorama(observation, time_step, "depth_sensor")

            if self.enable_semantic:
                self.display_panorama(observation, time_step, "semantic_sensor")

            if self.enable_rgb_depth:
                self.display_panorama(observation, time_step, "color_sensor")
        else:
            self.display_observations(observation, time_step)

    # gym close function
    def close(self):
        self.simulator.close()

    def query_shortest_path(self, start_loc, goal_loc):
        # Use ShortestPath module to compute path between samples.
        self.shortest_path = habitat_sim.ShortestPath()
        # set the start and goal locations
        self.shortest_path.requested_start = start_loc
        self.shortest_path.requested_end = goal_loc
        # find the path use the built-in path finder
        self.simulator.pathfinder.find_path(self.shortest_path)

    def display_shortest_path(self):
        # render top down map
        top_down_map = self.top_down_map.copy()
        grid_dimensions = (top_down_map.shape[0], top_down_map.shape[1])
        # convert world trajectory points to maps module grid points
        trajectory = [
            maps.to_grid(
                path_point[2],
                path_point[0],
                grid_dimensions,
                pathfinder=self.simulator.pathfinder,
            )
            for path_point in self.shortest_path.points
        ]
        grid_tangent = mn.Vector2(
            trajectory[1][1] - trajectory[0][1], trajectory[1][0] - trajectory[0][0]
        )
        path_initial_tangent = grid_tangent / grid_tangent.length()
        initial_angle = math.atan2(path_initial_tangent[0], path_initial_tangent[1])
        # draw the agent and trajectory on the map
        maps.draw_path(top_down_map, trajectory)
        maps.draw_agent(
            top_down_map, trajectory[0], initial_angle, agent_radius_px=8
        )
        return top_down_map

    @staticmethod
    def crop_local_map(agent_loc, global_map, size):
        # get the size of the global map
        r, c, _ = global_map.shape

        # compute the boundary
        crop_from_r = agent_loc[0] - size if agent_loc[0] - size > 0 else 0
        crop_from_c = agent_loc[1] - size if agent_loc[1] - size > 0 else 0

        crop_to_r = agent_loc[0] + size if agent_loc[0] + size < r else r
        crop_to_c = agent_loc[1] + size if agent_loc[1] + size < c else c

        # crop the map
        local_map = global_map[crop_from_r:crop_to_r, crop_from_c:crop_to_c, :]

        return local_map

    def draw_agent_on_top_down_map(self):
        # get the boundary of the environment
        top_down_map = self.top_down_map_traj.copy()
        grid_dimensions = (top_down_map.shape[0], top_down_map.shape[1])

        # convert the world positions to map locations
        agent_map_pos = maps.to_grid(
            self.agent.state.position[2],
            self.agent.state.position[0],
            grid_dimensions,
            pathfinder=self.simulator.pathfinder,
        )

        # plot the agent's path in dashed red color
        if self.plot_traj:
            top_down_map[agent_map_pos[0] - 5: agent_map_pos[0] + 5,
                         agent_map_pos[1] - 5: agent_map_pos[1] + 5, 0] = 255
            top_down_map[agent_map_pos[0] - 5: agent_map_pos[0] + 5,
                         agent_map_pos[1] - 5: agent_map_pos[1] + 5, 1] = 100
            top_down_map[agent_map_pos[0] - 5: agent_map_pos[0] + 5,
                         agent_map_pos[1] - 5: agent_map_pos[1] + 5, 2] = 0

        # save the current trajectory
        self.top_down_map_traj = top_down_map.copy()

        # convert goal positions to map locations
        goal_map_pos = maps.to_grid(
            self.goal_loc[2],
            self.goal_loc[0],
            grid_dimensions,
            pathfinder=self.simulator.pathfinder
        )

        # get the orientation and reference axis
        agent_angle, refer_axis = quat_to_angle_axis(self.agent.state.rotation)

        # compute the direction vector
        head_vector = [np.cos(agent_angle), 0, np.sin(agent_angle)]

        if np.array_equal(refer_axis, np.array([0, 1, 0])):
            # rotate along x axis
            rot_mat_x = np.array([[1, 0, 0],
                                  [0, np.cos(-np.pi), -np.sin(-np.pi)],
                                  [0, np.sin(-np.pi), np.cos(-np.pi)]])
            head_vector = np.matmul(rot_mat_x, head_vector)

        # rotate along z axis
        rot_mat_z = np.array([[np.cos(np.pi), -np.sin(np.pi), 0],
                              [np.sin(np.pi), np.cos(np.pi), 0],
                              [0, 0, 1]])
        head_vector_map = np.matmul(rot_mat_z, head_vector)

        # compute the angle
        map_angle = np.arctan2(head_vector_map[2], head_vector_map[0])

        if self.map_show_agent:
            agent_radius = 15
            # draw the agent on the map
            maps.draw_agent(
                top_down_map, agent_map_pos, map_angle, agent_radius_px=agent_radius
            )  # the agent will be resize a region with radius = 5 pixelsex

            # draw the goal on the map
            if self.map_show_goal:
                goal_off_set = 15
                top_down_map[goal_map_pos[0]-goal_off_set:goal_map_pos[0]+goal_off_set,
                             goal_map_pos[1]-goal_off_set:goal_map_pos[1]+goal_off_set, :] = 0.7

        # crop the local map out if the local map flag is True
        if self.enable_local_map:
            top_down_map = self.crop_local_map(agent_loc=agent_map_pos,
                                               global_map=top_down_map,
                                               size=self.local_map_size)

            if self.enable_ego_local_map:  # if True, rotate the local map based on agent orientation
                # rotate the local map into egocentric view
                rot_control = int(np.round(map_angle / (np.pi / 2)))
                if rot_control == 0:
                    top_down_map = np.rot90(top_down_map, k=2)
                elif rot_control == 1:
                    top_down_map = np.rot90(top_down_map, k=1)
                elif rot_control == -1:
                    top_down_map = np.rot90(top_down_map, k=3)
                else:
                    pass

        return top_down_map

    def display_observations(self, obs, t):
        if t == 0:  # create display artists for all sensors
            view_num = len(self.sensor_names)
            self.fig, self.arr = plt.subplots(1, view_num, figsize=(12, 6))
            self.arr = [self.arr] if view_num == 1 else self.arr
            # create the plot artists
            for i, name in enumerate(self.sensor_names):
                # remove the axis labels
                self.arr[i].axis("off")
                # add title
                self.arr[i].set_title(name)
                if name == 'color_sensor':
                    self.artists.append(self.arr[i].imshow(obs[name][:, :, 0:3]))
                elif name == "depth_sensor":
                    if self.depth_clip_vmax:
                        self.artists.append(self.arr[i].imshow(obs[name]))
                    else:
                        self.artists.append(self.arr[i].imshow(obs[name]))
                elif name == "top_down":
                    self.artists.append(self.arr[i].imshow(obs[name]))
                elif name == "semantic_sensor":
                    semantic_img = Image.new("P", (obs[name].shape[1], obs[name].shape[0]))
                    semantic_img.putpalette(d3_40_colors_rgb.flatten())
                    semantic_img.putdata((obs[name].flatten() % 40).astype(np.uint8))
                    semantic_img = semantic_img.convert("RGBA")
                    self.artists.append(self.arr[i].imshow(semantic_img))
                else:
                    raise Exception(f"Invalid observation name {name}")
        else:
            for i, name in enumerate(self.sensor_names):
                if name == 'color_sensor':
                    self.artists[i].set_data(obs[name][:, :, 0:3])
                elif name == 'depth_sensor':
                    self.artists[i].set_data(obs[name])
                elif name == "top_down":
                    self.artists[i].set_data(obs[name])
                elif name == "semantic_sensor":
                    semantic_img = Image.new("P", (obs[name].shape[1], obs[name].shape[0]))
                    semantic_img.putpalette(d3_40_colors_rgb.flatten())
                    semantic_img.putdata((obs[name].flatten() % 40).astype(np.uint8))
                    semantic_img = semantic_img.convert("RGBA")
                    self.artists[i].set_data(semantic_img)

        self.fig.canvas.draw()
        plt.pause(0.1)

    def display_panorama(self, obs, t, obs_type):
        if t == 0:
            if not self.enable_rgb_depth:
                # create the figure
                self.fig, self.arr = plt.subplots(3, 3, figsize=(12, 12))
                # remove the axis labels
                for m in range(3):
                    for n in range(3):
                        self.arr[m, n].axis("off")

                # built-in function
                def create_artist(plot_arr, r, c, obs_name):
                    # set title of the plot
                    plot_arr[r, c].set_title(obs_name)
                    # create the artist for subplot
                    if obs_type == "color_sensor":
                        return plot_arr[r, c].imshow(obs[obs_name][:, :, 0:3])
                    elif obs_type == "depth_sensor":
                        if self.depth_clip_vmax:
                            return plot_arr[r, c].imshow(obs[obs_name])
                        else:
                            return plot_arr[r, c].imshow(obs[obs_name])
                    elif obs_type == "semantic_sensor":
                        _img = Image.new("P", (obs[obs_name].shape[1], obs[obs_name].shape[0]))
                        _img.putpalette(d3_40_colors_rgb.flatten())
                        _img.putdata((obs[obs_name].flatten() % 40).astype(np.uint8))
                        _img = _img.convert("RGBA")
                        return plot_arr[r, c].imshow(_img)
                    else:
                        raise Exception(f"Invalid observation name {obs_type}")

                # add title
                self.artists.append(create_artist(self.arr, 0, 1, obs_type + '_front'))
                self.artists.append(create_artist(self.arr, 1, 0, obs_type + '_left'))
                self.artists.append(create_artist(self.arr, 1, 2, obs_type + '_right'))
                self.artists.append(create_artist(self.arr, 2, 1, obs_type + '_back'))

                # add top down observation
                self.artists.append(self.arr[1, 1].imshow(obs["top_down"]))
            else:
                # create the figure
                self.fig, self.arr = plt.subplots(4, 3, figsize=(12, 12))
                # remove the axis labels
                for m in range(4):
                    for n in range(3):
                        self.arr[m, n].axis("off")

                # add title
                self.arr[0, 0].set_title("color_front")
                self.arr[0, 1].set_title("depth_front")
                self.artists.append(self.arr[0, 0].imshow(obs['color_sensor_front']))
                self.artists.append(self.arr[0, 1].imshow(obs['depth_sensor_front']))

                self.arr[1, 0].set_title("color_left")
                self.arr[1, 1].set_title("depth_left")
                self.artists.append(self.arr[1, 0].imshow(obs['color_sensor_left']))
                self.artists.append(self.arr[1, 1].imshow(obs['depth_sensor_left']))

                self.arr[2, 0].set_title("color_right")
                self.arr[2, 1].set_title("depth_right")
                self.artists.append(self.arr[2, 0].imshow(obs['color_sensor_right']))
                self.artists.append(self.arr[2, 1].imshow(obs['depth_sensor_right']))

                self.arr[3, 0].set_title("color_back")
                self.arr[3, 1].set_title("depth_back")
                self.artists.append(self.arr[3, 0].imshow(obs['color_sensor_back']))
                self.artists.append(self.arr[3, 1].imshow(obs['depth_sensor_back']))

                # add top down observation
                self.arr[1, 2].set_title("top_down")
                self.artists.append(self.arr[1, 2].imshow(obs["top_down"]))
        else:
            if not self.enable_rgb_depth:
                # update the artists
                def update_artist(artists, idx, obs_name):
                    if obs_type == "color_sensor":
                        artists[idx].set_data(obs[obs_name][:, :, 0:3])
                    elif obs_type == "depth_sensor":
                        artists[idx].set_data(obs[obs_name])
                    elif obs_type == "semantic_sensor":
                        _img = Image.new("P", (obs[obs_name].shape[1], obs[obs_name].shape[0]))
                        _img.putpalette(d3_40_colors_rgb.flatten())
                        _img.putdata((obs[obs_name].flatten() % 40).astype(np.uint8))
                        _img = _img.convert("RGBA")
                        artists[idx].set_data(_img)
                    else:
                        raise Exception(f"Invalid observation name {obs_type}")
                # update the plot with the latest offline_dataset
                update_artist(self.artists, 0, obs_type + "_front")
                update_artist(self.artists, 1, obs_type + "_left")
                update_artist(self.artists, 2, obs_type + "_right")
                update_artist(self.artists, 3, obs_type + "_back")

                # update the top down view
                self.artists[-1].set_data(obs['top_down'])
            else:
                self.artists[0].set_data(obs['color_sensor_front'])
                self.artists[1].set_data(obs['depth_sensor_front'])

                self.artists[2].set_data(obs['color_sensor_left'])
                self.artists[3].set_data(obs['depth_sensor_left'])

                self.artists[4].set_data(obs['color_sensor_right'])
                self.artists[5].set_data(obs['depth_sensor_right'])

                self.artists[6].set_data(obs['color_sensor_back'])
                self.artists[7].set_data(obs['depth_sensor_back'])

                # add top down observation
                self.artists[8].set_data(obs["top_down"])

        self.fig.canvas.draw()
        plt.pause(0.1)

    # display a top down map
    def display_top_down_map(self):
        plt.figure(figsize=(12, 8))
        ax = plt.subplot(1, 1, 1)
        ax.axis("off")
        if len(self.top_down_map.shape) != 3:
            rows, cols = self.top_down_map.shape
        else:
            rows, cols, _ = self.top_down_map.shape
        plt.title(f"Size {rows} x {cols}")
        plt.imshow(self.top_down_map)
        plt.show()

    # rgb2gray
    @staticmethod
    def rgb2gray(obs):
        return rgb2gray(obs)

    def is_terminal(self, loc):
        # convert location to [x, y]
        agent_loc = np.array([loc[0], loc[2]])
        goal_loc = np.array([self.goal_loc[0], self.goal_loc[2]])
        # compute the geometric distance
        self.agent_goal_dist = np.linalg.norm(agent_loc - goal_loc)

        # check termination
        if self.agent_goal_dist < self.dist_eps or self.step_counter == self.max_episode_length:
            done = True
        else:
            done = False

        # return termination flag
        return done

    @staticmethod
    def quaternion_to_angle(quaternion_array):
        quaternion = quat_from_coeffs(quaternion_array)
        # get the orientation and reference axis
        agent_angle, refer_axis = quat_to_angle_axis(quaternion)

        # compute the direction vector
        head_vector = [np.cos(agent_angle), 0, np.sin(agent_angle)]

        if np.array_equal(refer_axis, np.array([0, 1, 0])):
            # rotate along x axis
            rot_mat_x = np.array([[1, 0, 0],
                                  [0, np.cos(-np.pi), -np.sin(-np.pi)],
                                  [0, np.sin(-np.pi), np.cos(-np.pi)]])
            head_vector = np.matmul(rot_mat_x, head_vector)

        # rotate along z axis
        rot_mat_z = np.array([[np.cos(np.pi), -np.sin(np.pi), 0],
                              [np.sin(np.pi), np.cos(np.pi), 0],
                              [0, 0, 1]])
        head_vector_map = np.matmul(rot_mat_z, head_vector)

        # compute the angle
        map_angle = np.arctan2(head_vector_map[2], head_vector_map[0])

        return map_angle


import sys
import os

sys.path.insert(0, "../app/nussl")

import nussl
import sklearn
import numpy as np
import librosa

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import time
import matplotlib.dates as md
from matplotlib.colors import LogNorm
from matplotlib import ticker
from matplotlib import gridspec

from MulticoreTSNE import MulticoreTSNE as TSNE

from . import clustering_test as ct

RUN_DC = False

def main():
    model_path = "/Users/ethanmanilow/Documents/School/Research/audio_representations/website/backend/models/data/models/deep_clustering_vocals_44k_long.model"
    cutoff = -40
    output_dir = os.path.join("output", "tdc_test")

    # for song_path in dsd_folder:
    # Load 'em all into memory
    song_path = "/Users/ethanmanilow/Documents/School/Research/audio_representations/website/backend/scripts/test_files"
    mix_path = os.path.join(song_path, "mix.wav")
    vox_path = os.path.join(song_path, "vox.wav")
    bk_path = os.path.join(song_path, "gtr.wav")
    mix = nussl.AudioSignal(mix_path)
    mix.to_mono(overwrite=True)
    vox = nussl.AudioSignal(vox_path)
    vox.to_mono(overwrite=True)
    bak = nussl.AudioSignal(bk_path)
    bak.to_mono(overwrite=True)

    n_bins = 100

    if RUN_DC:
        gt_vox_mask, gt_bak_mask, sm = ct.mel_mask(
            mix,
            vox,
            bak,
            mix.sample_rate,
            bg_mask_inverse=False,
            silence_mask_cutoff=cutoff,
        )

        dc = nussl.DeepClustering(
            mix,
            model_path=model_path,
            mask_type="binary",
            do_mono=True,
            return_mel_masks=True,
            pca_before_clustering=False,
            cutoff=cutoff,
        )
        dc_vox_mask, dc_bk_mask, binned, mel, scaled = ct.deep_clustering_mask(dc)
        dc_vox_mask, dc_bk_mask = dc_vox_mask.get_channel(0), dc_bk_mask.get_channel(0)

    else:
        dc_vox_mask = np.load(os.path.join("pickles", "dc_vox_mask.npy"))
        dc_bk_mask = np.load(os.path.join("pickles", "dc_bk_mask.npy"))
        binned = np.load(os.path.join("pickles", "binned.npy"))
        scaled = np.load(os.path.join("pickles", "scaled.npy"))

        tdc_space = np.zeros((n_bins, dc_vox_mask.shape[0]))

        for (i, j), tf_list in np.ndenumerate(binned):
            for item in tf_list:
                t, f = get_coordinate_from_TF_index(item, dc_vox_mask.shape[1])
                tdc_space[j, t] += 1

        plot_tdc(tdc_space, mix.time_vector[-1], output_dir, "tdc_test3.png")

def tsne_test():
    model_path = "/Users/ethanmanilow/Documents/School/Research/audio_representations/website/backend/models/data/models/deep_clustering_vocals_44k_long.model"
    cutoff = -40
    output_dir = os.path.join("output", "tsne_test")

    # for song_path in dsd_folder:
    # Load 'em all into memory
    song_path = "/Users/ethanmanilow/Documents/School/Research/audio_representations/website/backend/scripts/test_files"
    mix_path = os.path.join(song_path, "mix.wav")
    vox_path = os.path.join(song_path, "vox.wav")
    bk_path = os.path.join(song_path, "gtr.wav")
    mix = nussl.AudioSignal(mix_path)
    mix.to_mono(overwrite=True)
    vox = nussl.AudioSignal(vox_path)
    vox.to_mono(overwrite=True)
    bak = nussl.AudioSignal(bk_path)
    bak.to_mono(overwrite=True)

    dc = nussl.DeepClustering(
        mix,
        model_path=model_path,
        mask_type="binary",
        do_mono=True,
        return_mel_masks=True,
        pca_before_clustering=False,
        cutoff=cutoff,
    )
    dc.run()

    tsne = TSNE(verbose=2, n_jobs=6)
    tsne_space = tsne.fit_transform(dc.embeddings)

    plt.close("all")

    hm = plt.imshow(
        tsne_space
    )  # , norm=LogNorm(vmin=1, vmax=np.max([np.max(vox_diff), np.max(bg_diff)])))
    plt.title(r"TSNE Space")
    plt.colorbar(hm)

    plt.savefig(os.path.join(output_dir, "{}_tsne.png".format("test")))

def get_coordinate_from_TF_index(index, inner_dim):  # inner_dim == 150
    return index / inner_dim, index % inner_dim

def attempt2(scaled, dc_vox_mask, output_dir, mix):
    scaled_reshape = scaled.reshape(dc_vox_mask.shape[0], dc_vox_mask.shape[1], 2)
    scaled_axis = scaled_reshape[:, :, 0]
    tdc_space = np.array(
        [scaled_axis[t, :] for t in range(dc_vox_mask.shape[0])], dtype=float
    ).T

    plot_tdc(tdc_space, mix.time_vector[-1], output_dir, "tdc_test2.png")

def attempt1(dc, mix, output_dir):
    pca = sklearn.decomposition.PCA(n_components=1)
    n_bins = 75
    bins = np.arange(-1.0, 1.0, n_bins)
    dc_vox_mask, dc_bk_mask = dc.run()
    pca_embeddings = pca.fit_transform(dc.embeddings)

    dc_vox_mask, dc_bk_mask = dc_vox_mask.get_channel(0), dc_bk_mask.get_channel(0)

    pca_embeddings_reshape = pca_embeddings.reshape(dc_vox_mask.shape)

    tdc_space = np.array(
        [
            np.histogram(pca_embeddings_reshape[t, :], bins=n_bins)[0]
            for t in range(dc_vox_mask.shape[0])
        ]
    ).T

    plot_tdc(tdc_space, mix.time_vector[-1], output_dir, "tdc_test1.png")

def plot_tdc(tdc, t_max, output_dir, name):
    plt.close("all")
    fig = plt.figure(1, figsize=(64, 9))
    plt.suptitle("Temporal Deep Clustering", fontsize=40)
    tdc_heatmap_ax = plt.axes([0.15, 0.1, 0.8, 0.8])
    tdc_sum_ax = plt.axes([0.025, 0.1, 0.1, 0.8])

    tdc_log = librosa.amplitude_to_db(tdc.astype("float") + 1e-7)
    time_vect = np.linspace(0.0, t_max, tdc.shape[1])
    mel_vect = np.arange(0.0, tdc.shape[0])
    x, y = np.meshgrid(time_vect, mel_vect)
    hm = tdc_heatmap_ax.contourf(x, y, np.flipud(tdc_log))

    xfmt = matplotlib.ticker.FuncFormatter(
        lambda sec, x: time.strftime("%M:%S", time.gmtime(sec))
    )
    tdc_heatmap_ax.xaxis.set_major_formatter(xfmt)
    tdc_heatmap_ax.xaxis.set_ticks(np.linspace(0.0, t_max, 48))
    cb_axes = fig.add_axes([0.97, 0.1, 0.01, 0.8])
    fig.colorbar(hm, cax=cb_axes)

    tdc_sum_ax.fill_between(
        np.sum(tdc, axis=1)[::-1], np.arange(tdc.shape[0]), step="pre"
    )
    tdc_sum_ax.set_ylim(0, tdc.shape[0])
    plt.savefig(os.path.join(output_dir, name))

def playground():

    rand = np.random.rand(150, 1324, 40)

    rand_trans = rand.reshape(rand.shape[0], rand.shape[1] * rand.shape[2])
    standardized = sklearn.preprocessing.StandardScaler().fit_transform(rand_trans)

    pca = sklearn.decomposition.PCA(n_components=2)

    fit = pca.fit_transform(standardized)

    i = 0

if __name__ == "__main__":
    tsne_test()


# -*- coding: utf-8 -*-
"""
Created on Tue Jan 11 09:07:34 2022

@author: anton
"""

# Import the game
import gym_super_mario_bros
# Import the Joypad wrapper
from nes_py.wrappers import JoypadSpace
# Import the SIMPLIFIED controls
from gym_super_mario_bros.actions import SIMPLE_MOVEMENT

#%%
env = gym_super_mario_bros.make('SuperMarioBros-v0')
env = JoypadSpace(env, SIMPLE_MOVEMENT)
#%%
import gym
# Import Frame Stacker Wrapper and GrayScaling Wrapper
from gym.wrappers import GrayScaleObservation, FrameStack
# Import Vectorization Wrappers
from stable_baselines3.common.vec_env import VecFrameStack, DummyVecEnv
# Import Matplotlib to show the impact of frame stacking
from matplotlib import pyplot as plt
#%%
plt.figure()
plt.imshow(state[0])
#%%
import torch
import numpy as np
class SkipFrame(gym.Wrapper):
    def __init__(self, env, skip):
        super().__init__(env)
        self._skip = skip

    def step(self, action):
        total_reward = 0.0
        done = False
        for i in range(self._skip):
            obs, reward, done, info = self.env.step(action)
            total_reward += reward
            if done:
                break
        return obs, total_reward, done, info

from torchvision import transforms
from torchvision.transforms.functional import crop
from gym.spaces import Box

class GrayScaleObservation(gym.ObservationWrapper):
    def __init__(self, env):
        super().__init__(env)
        self.observation_space = Box(low=0, high=255, shape=self.observation_space.shape[:2], dtype=np.uint8)

    def observation(self, observation):
        transform = transforms.Grayscale()
        return transform(torch.tensor(np.transpose(observation*[1, 0, 0], (2, 0, 1)).copy(), dtype=torch.float))

def mycrop(image):
    return crop(image, 50, 0, 160, 400)

class ResizeObservation(gym.ObservationWrapper):
    def __init__(self, env, shape):
        super().__init__(env)
        self.shape = (shape, shape)
        obs_shape = self.shape + self.observation_space.shape[2:]
        self.observation_space = Box(low=0, high=255, shape=obs_shape, dtype=np.uint8)

    def observation(self, observation):
        transformations = transforms.Compose([transforms.Lambda(mycrop), transforms.Resize(self.shape), transforms.Normalize(0, 255)])
        return transformations(observation).squeeze(0)

#%%
# 1. Create the base environment
env = gym_super_mario_bros.make('SuperMarioBros-v0')
# 2. Simplify the controls
# env = JoypadSpace(env, [["right"], ["right", "A"]])
env = JoypadSpace(env, SIMPLE_MOVEMENT)

# 3. Grayscale
env = FrameStack(ResizeObservation(GrayScaleObservation(SkipFrame(env, skip=4)), shape=48), num_stack=4)

state = env.reset()
#%%
state = env.reset()
state, reward, done, info = env.step(["right"])

plt.figure(figsize=(20,16))
for idx in range(state.shape[0]):
    plt.subplot(1,4,idx+1)
    plt.imshow(state[0])
plt.show()
#%%
import os
# Import PPO for algos
from stable_baselines3 import PPO
# Import Base Callback for saving models
from stable_baselines3.common.callbacks import BaseCallback
#%%
class TrainAndLoggingCallback(BaseCallback):

    def __init__(self, check_freq, save_path, verbose=1):
        super(TrainAndLoggingCallback, self).__init__(verbose)
        self.check_freq = check_freq
        self.save_path = save_path

    def _init_callback(self):
        if self.save_path is not None:
            os.makedirs(self.save_path, exist_ok=True)

    def _on_step(self):
        if self.n_calls % self.check_freq == 0:
            model_path = os.path.join(self.save_path, 'best_model_{}'.format(self.n_calls))
            self.model.save(model_path)

        return True
#%%
CHECKPOINT_DIR = './train/'
LOG_DIR = './logs/'
#%%
callback = TrainAndLoggingCallback(check_freq=10000, save_path=CHECKPOINT_DIR)
#%%
model = PPO('CnnPolicy', env, verbose=1, tensorboard_log=LOG_DIR, learning_rate=1e-5,
            n_steps=512, batch_size=512, n_epochs=16, clip_range=0.2, target_kl=0.01)
# model = PPO.load('./train/best_model_30000')
#%%
model.set_env(env)
model.learn(total_timesteps=1e7, callback=callback)
#%%
model = PPO.load('./train/best_model_2880000')

#%%
# Start the game
state = env.reset()
# Loop through the game
while True:
    action, _ = model.predict(np.array(state))
    state, reward, done, info = env.step(action)
    env.render()
env.close()
#%%
# state = env.reset()
action, _ = model.predict(np.array(state))
state, reward, done, info = env.step(action)

plt.figure(figsize=(20,16))
for idx in range(state.shape[0]):
    plt.subplot(1,4,idx+1)
    plt.imshow(state[idx])
plt.show()


import pygame
import sys

pygame.init()

width, height = 800, 800
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("DTT")

rows = 20
columns = 20

active = True

boxes = []
drawn = False

counter = 0

def draw_grid(screen, width, height):
    """
    This draws the grid lines
    First For loop is for the vertical lines
    second For loop is for the horizontal lines
    """

    for x in range(0, width, 40):
        pygame.draw.line(screen, (0, 0, 0), (x, 0), (x, height))
    for y in range(0, height, 40):
        pygame.draw.line(screen, (0, 0, 0), (0, y), (width, y))

def draw_boxes(screen, width, height):
    """
    This draws the individual boxes
    It also appends the individual boxes from top to bottom going towards the right
    """

    for i in range(rows):
        for j in range(columns):
            pygame.draw.rect(screen, (0, 255, 0), (i * 40, j * 40, 40, 40))
            boxes.append((screen, [0, 255, 0], (i * 40, j * 40, 40, 40)))
    return boxes

def hand_draw(screen, width, height):
    """
    This changes the color of the clicked box to red
    """

    for box in boxes:
        if box[1] == [255, 0, 0]:
            pygame.draw.rect(screen, (255, 0, 0), box[2])

while active:

    cursor = pygame.mouse.get_pos()

    if drawn == False: # Stops the green boxes from drawing again
        draw_boxes(screen, width, height)
        drawn = True

    hand_draw(screen, width, height)
    draw_grid(screen, width, height)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            active = False
            pygame.quit()
            sys.exit()

        if event.type == pygame.KEYDOWN:
            char_hold = {}
            if event.key == pygame.K_SPACE:
                with open("grid.txt", "w", encoding="utf-8") as f:
                    for i, box in enumerate(boxes):

                        if i < 20: # This is basically creating the outline of the dictionary

                            if box[1] == [255, 0, 0]: # If the box is red, then we will add it to the dictionary as an " ▢ "
                                char_hold[i] = "▢"
                            else:
                                char_hold[i] = "▥"  # If the box is green, then we will add it to the dictionary as an " ▥ "
                        else:
                            if counter == 20:  # This is resetting the counter so then values over 20 can be added to the dictionary
                                counter = 0

                            if box[1] == [255, 0, 0]: # This is now adding to the dictionary instead of setting it
                                char_hold[counter] += "▢"
                            else:
                                char_hold[counter] += "▥"

                            counter += 1

                    for i in char_hold:
                        f.write((char_hold[i]) + "\n") # prints each key on separate lines.

        if event.type == pygame.MOUSEBUTTONDOWN:
            for box in boxes:
                if pygame.Rect(box[2]).collidepoint(cursor): # box[2] are the coordinates of the box
                    box[1][0] = 255 # turns the green box into red
                    box[1][1] = 0 # Removes the green value

    pygame.display.update()


from typing import List
class Solution:
    def bitMagic(self, n : int, arr : List[int]) -> int:
        # code here
        ans = 0
        cnt = 0
        i=0
        j = len(arr)-1
        while (i<=j):
            if arr[i]!=arr[j]:
                cnt+=1
            i+=1
            j-=1
        if cnt==1:
            return (1)
        if cnt%2!=0:
            cnt+=1
        return (cnt//2)


from message import response_list, notify_message
from notify import notify_recepients
import logging
import os
from datetime import datetime
from os.path import exists as file_exists
import shutil
from dotenv import load_dotenv

load_dotenv()

log_time = datetime.now().strftime('%H:%M:%S')

# check that the file with items and phone numbers exists in data folder
if not file_exists('/data/items_and_numbers.cfg'):
# if not file_exists('./data/items_and_numbers.cfg'):  # noqa E115
    print(f'{log_time} - items_and_numbers.cfg file not found in the data folder')
    exit()

# copy the file with the items and phone numbers to the working directory
shutil.copy('/data/items_and_numbers.cfg', '/app/items_and_numbers.py')
# shutil.copy('./data/items_and_numbers.cfg', './app/items_and_numbers.py')

# the file just copied is imported to this script as a python file
import items_and_numbers as itn  # noqa E402

# load the items and phone numbers from the recently imported file
products = itn.products
recepients = itn.recepients

# check urls and creates the SMS body
product_responses = response_list(products)
message = notify_message(product_responses)

if __name__ == "__main__":
    logging.basicConfig(
        filename='/data/productFinder.log',
        # filename='./data/productFinder.log',
        encoding='utf-8',
        format='%(asctime)s %(levelname)-8s %(message)s',
        level=logging.INFO,
        datefmt='%Y-%m-%d %H:%M:%S')

    # Set aws calls back to WARNING to avoid verbose messages
    logging.getLogger('botocore').setLevel(logging.WARNING)

    # load_dotenv()
    # check the existance of pre-requisites
    logging.info('----- BEGIN PROCESS -----')
    if os.getenv('AWS_ACCESS_KEY_ID') is None:
        print(f'{log_time} - AWS_ACCESS_KEY_ID not found in environment variables')
        logging.error('AWS_ACCESS_KEY_ID not found in environment variables ...')
        logging.info('check README for information... terminating')
        exit()
    if os.getenv('AWS_SECRET_ACCESS_KEY') is None:
        print(f'{log_time} - AWS_SECRET_ACCESS_KEY not found in environment variables')
        logging.error('AWS_SECRET_ACCESS_KEY not found in environment variables ...')
        logging.info('check README for information... terminating')
        exit()
    if os.getenv('AWS_REGION_NAME') is None:
        print(f'{log_time} - AWS_REGION_NAME not found in environment variables')
        logging.error('AWS_REGION_NAME not found in environment variables ...')
        logging.info('check README for information... terminating')
        exit()

    # check the structure of the products dictionary imported
    product_structure_is_valid = True
    if type(products) != dict:
        product_structure_is_valid = False
        for x, y in products.items():
            if type(y) != dict:
                product_structure_is_valid = False
            if 'store' not in y or 'name' not in y or 'url' not in y:
                product_structure_is_valid = False

    if product_structure_is_valid:
        pass
    else:
        print(f'{log_time} - products in the items_and_numbers.cfg structure incorrect...')
        logging.error('products in the items_and_numbers.cfg structure incorrect...')
        logging.info('check "items_and_numbers-sample.cfg" for correct structure... terminating')
        exit()

    # begin checking items
    print(f'{log_time} - checking inventory...')
    if len(message) > 0:
        notify_recepients(recepients, message)
    else:
        logging.info("no available items found")
        print(f'{log_time} - no available items found')


import warnings

import torch
from torch import nn

from rainy.lib.kfac import KfacPreConditioner, Layer

class Flatten(nn.Module):
    def forward(self, x):
        return x.view(x.size(0), -1)

def test_kfac():
    in_shape = (10, 4, 8, 8)
    cnn = nn.Conv2d(4, 8, 4)
    fc = nn.Linear(8 * 5 * 5, 10)
    net = nn.Sequential(cnn, Flatten(), fc)
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        precond = KfacPreConditioner(net)
    input_ = torch.randn(*in_shape).requires_grad_()
    out = net(input_)
    loss = nn.MSELoss()(out, torch.randn(in_shape[0], 10))
    with precond.save_grad():
        loss.backward()
    precond.step()
    for group in precond.param_groups:
        state = precond.state[group["params"][0]]
        if group["layer_type"] is Layer.CONV2D:
            assert state["eg*ex"].shape == torch.Size((8, 65))
        else:
            assert state["eg*ex"].shape == torch.Size((10, 201))


"""
1305. 两棵二叉搜索树中的所有元素
给你 root1 和 root2 这两棵二叉搜索树。请你返回一个列表，其中包含 两棵树 中的所有整数并按 升序 排序。.
"""
from typing import List
from libraries.utils import TreeNode

def getAllElements(root1: TreeNode, root2: TreeNode) -> List[int]:
    def traverse(node: TreeNode, numList: List[int]):
        if not node:
            return
        traverse(node.left, numList)
        numList.append(node.val)
        traverse(node.right, numList)
        return

    root1List, root2List = [], []
    traverse(root1, root1List)
    traverse(root2, root2List)
    root1Index, root2Index = 0, 0
    result = []
    while root1Index < len(root1List) or root2Index < len(root2List):
        if root1Index == len(root1List):
            result.append(root2List[root2Index])
            root2Index += 1
        elif root2Index == len(root2List):
            result.append(root1List[root1Index])
            root1Index += 1
        else:
            if root1List[root1Index] == root2List[root2Index]:
                result.append(root1List[root1Index])
                result.append(root1List[root1Index])
                root1Index += 1
                root2Index += 1
            elif root1List[root1Index] < root2List[root2Index]:
                result.append(root1List[root1Index])
                root1Index += 1
            else:
                result.append(root2List[root2Index])
                root2Index += 1
    return result

a = TreeNode(2)
a.left = TreeNode(1)
a.right = TreeNode(4)
b = TreeNode(1)
b.left = TreeNode(0)
b.right = TreeNode(3)
getAllElements(a, b)

1305. 两棵二叉搜索树中的所有元素.py


from django.shortcuts import render, redirect
from django.views.generic import TemplateView
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView
from django.db import connection

from teklando.forms import CustomUserCreationForm
from teklando.models import Voluntario
from teklando.models import Sugerido

# Create your views here.

class HomePageView(TemplateView):
    template_name = 'index.html'

class SignUpView(CreateView):
    form_class = CustomUserCreationForm
    success_url = reverse_lazy('login')
    template_name = 'cadastro.html'

def AtividadePageView(request):
    current_user = request.user
    print(current_user.username)
    cursor = connection.cursor()
    cursor.execute("SELECT v.username, a.nome, a.horario, a.descricao, e.nome FROM teklando_atividade_escolas tae INNER JOIN teklando_atividade a ON a.id = tae.atividade_id INNER JOIN teklando_escola e ON e.id = tae.escola_id INNER JOIN teklando_voluntario_atividades tva ON tva.atividade_id = a.id INNER JOIN teklando_voluntario v ON tva.voluntario_id = v.id WHERE v.username = %s", [current_user.username])
    row = cursor.fetchone()
    print('=======================')
    print(row)
    print('=======================')
    if row is not None:
        nome_atividade = row[1]
        horario_atividade = row[2]
        descricao_atividade = row[3]
        nome_escola = row[4]
        contexto = {'nome_atividade': nome_atividade,
                    'horario_atividade': horario_atividade,
                    'descricao_atividade': descricao_atividade,
                    'nome_escola': nome_escola,
                    }
        return render(request, 'atividades.html', contexto)
    else:
        msg = 'Você ainda não tem atividades cadastradas'
        print(msg)
        contexto = {'msg': msg}
        return render(request, 'atividades.html', contexto)

def VoluntarioPageView(request):
    current_user = request.user
    print(current_user.id)
    sugeridos = Sugerido.objects.select_related('atividades', 'voluntarios').filter(voluntarios__in=Voluntario.objects.filter(username=current_user.username))
    contexto = {'sugeridos': sugeridos}
    return render(request, 'voluntario.html', contexto)

def Cadastrar_Atividade(request, id):
    current_user = request.user
    experience.valores = request.POST.get('valor')
    print(current_user.id)
    cursor = connection.cursor()
    cursor.execute('INSERT INTO teklando_voluntario_atividades(voluntario_id, atividade_id) VALUES(%s, %s)', [current_user.id, id])
    return redirect('/atividades')


from django.shortcuts import render

from .utils import get_turn_info
from django.contrib import messages
from django.http.response import JsonResponse
from django.shortcuts import render

from .models import Record, RoomMember
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def record(request):
    if request.method == "POST":
        print("file saving start--------=========")
        audio_file = request.FILES.get("video")
        language = request.POST.get("language")
        record = Record.objects.create(language=language, voice_record=audio_file)
        record.save()
        messages.success(request, "Audio recording successfully added!")
        print("\n\n\n=============================\nrecording saved in database",record,"\n\n\n")
        return JsonResponse(
            {
                "url": record.get_absolute_url(),
                "success": True,
            }
        )
    context = get_turn_info()
    return render(request, "chat/peer.html", context)

def peer(request):
    # get numb turn info
    context = get_turn_info()
    print('context context context context: ', context)

    return render(request, 'chat/peer.html', context=context)

def create_or_join(request):
    if request.method == "POST":
        roomname = request.POST.get("roomname")
        try:
            if RoomMember.objects.get(room_name = roomname):
                return render(request,'chat/index.html',{'error':f'Room Already Exists with name - '+roomname})
        except:
            pass
        room = RoomMember.objects.create(room_name = roomname)
        room.save()
        return render(request,'chat/peer.html',{'roomname':roomname,'isCreater':True})

    return render(request,'chat/index.html')

def temp_redirect(request):
    print("inside temp_redirect")
    return render(request,'main.html',{'error':'Room Not Exists!'})


'''
  File name: feat_match.py
  Author:
  Date created:
'''

'''
  File clarification:
    Matching feature descriptors between two images. You can use k-d tree to find the k nearest neighbour.
    Remember to filter the correspondences using the ratio of the best and second-best match SSD. You can set the threshold to 0.6.
    - Input descs1: 64 × N1 matrix representing the corner descriptors of first image.
    - Input descs2: 64 × N2 matrix representing the corner descriptors of second image.
    - Outpuy match: N1 × 1 vector where match i points to the index of the descriptor in descs2 that matches with the
                    feature i in descriptor descs1. If no match is found, you should put match i = −1.
'''

from annoy import AnnoyIndex

import numpy as np

THRES = 0.6

def feat_match(descs1, descs2):
  # Your Code Here

  descs1T = np.transpose(descs1)
  descs2T = np.transpose(descs2)

  an = AnnoyIndex(descs1.shape[0])

  for vecIndex in range(descs2T.shape[0]):
    an.add_item(vecIndex, descs2T[vecIndex, :])

  an.build(200)

  mathingIndex = []

  for vecIndex in range(descs1T.shape[0]):
    vector = descs1T[vecIndex, :];
    neighbours, distance = an.get_nns_by_vector(vector, 2, search_k=-1, include_distances=True)

    if distance[1]!= 0 and(1.0*distance[0])/distance[1] <= THRES:
      mathingIndex.append(neighbours[0])
    else:
      mathingIndex.append(-1)

  return np.array(mathingIndex)


import numpy as np
import torch
from settings import float_type

def gauss_legendre(n, a=-1., b=1.):
    """function to compute weights and abscissas for Gauss-Legendre quadrature """
    h = b - a
    x, w = np.polynomial.legendre.leggauss(n)

    # adjust integration limits
    if a != -1. or b != 1.:
        x = (x + 1.) * (h / 2.) + a
        w = (h / 2.) * w

    xt = torch.tensor(x).type(float_type)
    wt = torch.tensor(w).type(float_type)
    return xt, wt

def gauss_hermite(n):
    """function to compute weights and abscissas for Gauss-Hermite quadrature"""
    x, w = np.polynomial.hermite.hermgauss(n)
    xt = torch.tensor(x).type(float_type)
    wt = torch.tensor(w).type(float_type)

    return xt, wt


import pygame
import img_handling
BLACK = (0,0,0)
WHITE = (255,255,255)
(width, height) = pygame.Surface.get_size(img_handling.WOOD_img)
FONT_SIZE = int(width//2.5)
class Tile():
    def __init__(self, t_type, number, img, pos, coords, board=None):
        self.t_type = t_type
        self.number = number
        self.coords = coords
        self.vertices = []
        self.pos = pos
        self.img = img
        if self.number!=-1:
            self.num_disp = pygame.image.load("images/num" + str(self.number) + ".png")
            self.num_disp = pygame.transform.scale(self.num_disp, (FONT_SIZE, FONT_SIZE)).convert_alpha()
            self.fill_vertices(board)

    def draw_tile(self, screen):
        #outputs the tile to the screen with its number
        screen.blit(self.img, (self.pos))
        if self.number!=-1:
            screen.blit(self.num_disp, (int(self.pos[0]+width/2-FONT_SIZE/2), int(self.pos[1]+height/2-FONT_SIZE/2)))

    def name_breakdown(self):
        #breaks down the "coords" element from a string and returns all of the 6 vertices from it
        #takes the inbetween of the first 2 coords and matches it with either of the ends
        #takes the front and end of first 2 coords and matches it with either of the ones in between the last 2
        #for example "2 4 0 3" -> [[3,0],[3,3],[2,1],[2,2],[4,1],[4,2]]
        baseList = list(map(int,self.coords.split()))
        coordList = []
        coordList.append([baseList[0]+1, baseList[2]])
        coordList.append([baseList[0]+1, baseList[3]])
        coordList.append([baseList[0], baseList[2]+1])
        coordList.append([baseList[0], baseList[3]-1])
        coordList.append([baseList[1], baseList[2]+1])
        coordList.append([baseList[1], baseList[3]-1])
        return coordList

    def fill_vertices(self, board):
        #print("y len:", len(board),"x len:",len(board[0]))
        coordList = self.name_breakdown()
        for i in range(6):
            coord = coordList[i]
            #print(coord[0], coord[1])
            self.vertices.append(board[coord[0]][coord[1]])
        #print(board)

    def add_resources(self, o, r , b, w, deck):
        #is called once it is discovered that the number has been rolled, adds to the players hands the resource attached
        res = self.t_type.upper()
        for i in range(6):
            vertex = self.vertices[i]
            if vertex.colour != " ":
                if vertex.colour == "r":
                    if vertex.occ_by == "rv":
                        deck.draw_res(res, r)
                    else:
                        deck.draw_res(res, r)
                        deck.draw_res(res, r)
                elif vertex.colour == "b":
                    if vertex.occ_by == "bv":
                        deck.draw_res(res, b)
                    else:
                        deck.draw_res(res, b)
                        deck.draw_res(res, b)
                elif vertex.colour == "w":
                    if vertex.occ_by == "wv":
                        deck.draw_res(res, w)
                    else:
                        deck.draw_res(res, w)
                        deck.draw_res(res, w)
                elif vertex.colour == "o":
                    if vertex.occ_by == "ov":
                        deck.draw_res(res, o)
                    else:
                        deck.draw_res(res, o)
                        deck.draw_res(res, o)


from socket import gethostbyname, create_connection, error
from lib.core.utils import Colors
import sys

def check_wifi():
    """Check wifi connection status.

    Returns:
        True: Connection is ok.
        Exit: Connection is unstable.
    """
    try:
        gethostbyname("google.com")
        conexion = create_connection(("google.com", 80), 1)
        conexion.close()

        return True

    except error:
        sys.exit(Colors.FAIL + "[BashTube] " + Colors.RESET + "Check your internet connection.")