# -*- coding: utf-8 -*-
from biocluster.workflow import Workflow
import pandas as pd
import glob
import os
import json
import time
import re
from biocluster.file import getsize, exists
from biocluster.file import download
from mbio.packages.ref_rna_v2.functions import tryforgood
from mbio.packages.project_demo.run_log.get_run_log import GetRunLog
from biocluster.core.function import filter_error_info, link, CJsonEncoder
import shutil

class DiffexpBatchNewWorkflow(Workflow):
    """
    差异分析
    """
    def __init__(self, wsheet_object):
        self._sheet = wsheet_object
        super(DiffexpBatchNewWorkflow, self).__init__(wsheet_object)
        options = [
            dict(name='exp_type', type='string', default="tpm"),
            dict(name='exp_matrix', type='string'),
            dict(name="group_dict", type="string"),
            dict(name="diff_main_id", type="string"),
            dict(name="count", type="infile", format="lnc_rna.common"),
            dict(name="group", type="string"),
            dict(name="cmp", type="string"),
            dict(name="type", type="string"),
            dict(name="task_id", type="string"),
            dict(name="exp_level", type="string"),
            dict(name="seq_type_path",type="infile",format="lnc_rna.common"),#20190423 fwy新增，rna_seq_type文件路径
            # pvalue 统计判断阈值
            dict(name="pvalue", type="float", default=0.05),
            dict(name="pvalue_padjust", type="string", default="padjust"),
            dict(name="fc", type="float", default=2),
            dict(name="padjust_way", type='string', default="BH"),
            # method: DESeq2, edgeR, DEGseq
            dict(name="method", type="string", default="DESeq2"),
            dict(name="rna_type", type="string"),
            dict(name="filter_method", type="string", default=None),
            dict(name="tpm_filter_threshold", type="float", default="0"),
            # batch by zjx 20200917
            dict(name="is_batch", type="bool", default=False),
            dict(name="has_batch", type="bool"),
            dict(name="batch_matrix", type="infile", format="lnc_rna.common"),
            # noiseq
            dict(name='prob', type='float', default=0.8),
            # to update sg_status
            {"name": "update_info", "type": "string"},
        ]
        self.add_option(options)
        self.set_options(self._sheet.options())
        self.tool = self.add_tool("lnc_rna.diffexp_batch")
        # self.all_exp = self.api.api("lnc_rna.all_exp")
        self._sheet.output = self._sheet.output.replace('interaction_results',
                                                        'interaction_results/02 Diff_Express')
        self.inter_dirs = []

    def send_log(self, data):
        # 中间目录修改
        m = re.match("^([\w\-]+)://(.*)interaction_result.*$", self._sheet.output)
        region = m.group(1)
        inter_dir = m.group(2)
        self.logger.info("更新结果目录")

        if "dirs" in data["data"]["sync_task_log"]:
            for dir_path in self.inter_dirs:
                dir_dict = {
                    "path": os.path.join(inter_dir, "interaction_results", dir_path[0]),
                    "size": "",
                    "format": dir_path[1],
                    "description": dir_path[2],
                    "region": region,
                }
                if len(dir_path) >= 5:
                    dir_dict.update({"code": "D" + dir_path[5]})

                data["data"]["sync_task_log"]["dirs"].append(dir_dict)
        with open(self.work_dir + "/post.changed.json", "w") as f:
            json.dump(data, f, indent=4, cls=CJsonEncoder)
        super(DiffexpBatchNewWorkflow, self).send_log(data)

    def run(self):
        self.tool.on("end", self.run_uniform)
        self.get_run_log()
        self.run_tool()
        super(DiffexpBatchNewWorkflow, self).run()

    def get_run_log(self):
        get_run_log = GetRunLog("lnc_rna", table="sg_diff", main_id=self.option('diff_main_id'),
                                dir_path=self.work_dir)
        self.run_log = get_run_log.run()

    def set_db(self):
        """
        保存结果表到mongo数据库中
        """
        # add result info
        seq_type_path=self.option("seq_type_path").prop['path']
        self.stop_timeout_check()
        self.all_exp = self.api.api("lnc_rna.all_exp")
        # add result info
        self.all_exp.add_diffexp_all(self.uniform.output_dir, self.tool.output_dir,
                                     group_dict=self.option('group_dict'),
                                     main_id=self.option('diff_main_id'),
                                     diff_method=self.option('method'),
                                     create_geneset=False,
                                     pvalue_padjust=self.option('pvalue_padjust'),
                                     seq_type=seq_type_path,
                                     rna_type=self.option("rna_type")
                                     )
        self.paste_annotation()
        self.set_output()
        self.end()

    def end(self):
        if os.path.exists(os.path.join(self.tool.output_dir, os.path.basename(self.run_log))):
            os.remove(os.path.join(self.tool.output_dir, os.path.basename(self.run_log)))
        os.link(self.run_log, os.path.join(self.tool.output_dir, os.path.basename(self.run_log)))

        if os.path.exists(self.output_dir + '/upload'):
            os.remove(self.output_dir + '/upload')
        shutil.copytree(self.tool.output_dir, self.output_dir + '/upload')
        rm_files = glob.glob(self.output_dir + '/upload/*.DE.list')
        rm_files += glob.glob(self.output_dir + '/upload/*.normalize.xls')
        rm_files += glob.glob(self.output_dir + '/upload/*.sizeFactor.xls')
        rm_files += glob.glob(self.output_dir + '/upload/*.normFactor.xls')
        for each in rm_files:
            os.remove(each)

        result_dir = self.add_upload_dir(self.output_dir + '/upload')
        self.inter_dirs = [
            ["02 Diff_Express", "", "表达量差异分析结果目录", 0],
        ]
        result_dir.add_relpath_rules([
            [".", "", "基因表达量差异分析文件", 0, "211063"],
            ['run_parameter.txt', 'txt', '运行参数日志', 0]
        ])
        result_dir.add_regexp_rules([
            [r'.*_diff_summary.xls', 'xls', '表达量差异统计表', 0],
            [r'.*_vs_.*\..*\.xls', 'xls', '表达量差异详情表', 0],
            [r'.*_vs_.*\..*\.annot.xls', 'xls', '表达量差异注释表', 0],
        ])

        super(DiffexpBatchNewWorkflow, self).end()

    def set_output(self):
        diff_total=pd.read_table(os.path.join(self.tool.output_dir,"total_diff_stat.{}.xls".format(self.option("method").lower())),index_col="seq_id")
        diff_files_stat = glob.glob(os.path.join(self.tool.output_dir, '*.{}.xls'.format(self.option("method").lower())))
        df_anno = pd.read_table(diff_files_stat[0], index_col="seq_id")
        df1_genebase = pd.DataFrame(df_anno, columns=["length"])
        df1_geneanno=pd.DataFrame(df_anno,columns=["nr", "go", "KO_id", "KO_name", "paths", "cog", "cog_description", "pfam", "swissprot"])
        dff1 = pd.concat([df1_genebase, diff_total], axis=1)
        dff2 = pd.concat([dff1, df1_geneanno], axis=1)
        dff2.index.set_names("seq_id", inplace=True)
        dff2.to_csv(self.tool.output_dir + "/total_diff_stat.{}.xls".format(self.option("method").lower()), sep="\t")

    def run_tool(self):
        count_file=os.path.join(self.work_dir,"seqs_count.matrix")
        with open(self.option('count').prop['path'], "r") as cf:
            with open(count_file,"w") as ff:
                        firstline = cf.readline()
                        ff.write(firstline)
                        exp_records = cf.readlines()
                        if self.option('type') == "ref":
                            for record in exp_records:
                                line_info = record.strip().split()
                                if line_info[-1] == "false":
                                    if self.option('rna_type') == "lncRNA":
                                        numinfo1 = len(line_info)
                                        if line_info[numinfo1-2] == "lncRNA":
                                            ff.write(record)
                                    elif self.option('rna_type') == "mRNA":
                                        numinfo1 = len(line_info)
                                        if line_info[numinfo1-2] == "mRNA":
                                            ff.write(record)
                                    else:
                                        ff.write(record)
                        else:
                            for record in exp_records:
                                line_info = record.strip().split()
                                if self.option('rna_type') == "lncRNA":
                                    numinfo1 = len(line_info)
                                    if line_info[numinfo1-2] == "lncRNA":
                                        ff.write(record)
                                elif self.option('rna_type') == "mRNA":
                                    numinfo1 = len(line_info)
                                    if line_info[numinfo1-2] == "mRNA":
                                        ff.write(record)
                                else:
                                    ff.write(record)
        ff.close()
        count_matrix = pd.read_table(count_file)
        count_matrix = count_matrix.set_index('seq_id')
        # exp_matrix.drop(exp_matrix.columns[len(exp_matrix.columns) - 1], axis=1, inplace=True)
        count_matrix.drop(['rna_type'], axis=1, inplace=True)
        count_matrix.drop(['is_new'], axis=1, inplace=True)
        count_output = os.path.join(self.work_dir,"count_matrix")
        count_matrix.to_csv(count_output, sep='\t', header=True, index=True)
        print('success to export count matrix')
        opts = dict(
            exp=self.option('exp_matrix'),
            exp_type=self.option('exp_type'),
            count=count_output,
            group=self.option('group'),
            method=self.option('method'),
            cmp=self.option('cmp'),
            tpm_filter_threshold=self.option("tpm_filter_threshold"),
            is_batch=self.option('is_batch'),
            filter_method=self.option("filter_method"),
            no_filter=True,
            analysis='split',
            fc=self.option('fc'),
        )
        if self.option('is_batch') == False:
            if self.option("method").lower() in ["degseq", "edger", "deseq2", 'limma', 'svaseqlimma']:
                opts.update(
                    pvalue=self.option('pvalue'),
                    pvalue_padjust=self.option('pvalue_padjust'),
                    padjust_way=self.option('padjust_way'),
                )
            else:
                opts.update(
                    prob=self.option('prob')
                )
        else:
            if self.option('has_batch') == True:
                opts = dict(
                    exp=self.option('exp_matrix'),
                    exp_type=self.option('exp_type'),
                    count=count_output,
                    group=self.option('group'),
                    method=self.option('method'),
                    cmp=self.option('cmp'),
                    pvalue=self.option('pvalue'),
                    pvalue_padjust=self.option('pvalue_padjust'),
                    padjust_way=self.option('padjust_way'),
                    fc=self.option('fc'),
                    tpm_filter_threshold=self.option("tpm_filter_threshold"),
                    is_batch=self.option('is_batch'),
                    has_batch=self.option('has_batch'),
                    batch_matrix=self.option('batch_matrix'),
                    filter_method=self.option("filter_method"),
                    no_filter=True,
                    analysis='split'
                )
            else:
                opts = dict(
                    exp=self.option('exp_matrix'),
                    exp_type=self.option('exp_type'),
                    count=count_output,
                    group=self.option('group'),
                    method=self.option('method'),
                    cmp=self.option('cmp'),
                    pvalue=self.option('pvalue'),
                    pvalue_padjust=self.option('pvalue_padjust'),
                    padjust_way=self.option('padjust_way'),
                    fc=self.option('fc'),
                    tpm_filter_threshold=self.option("tpm_filter_threshold"),
                    is_batch=self.option('is_batch'),
                    has_batch=self.option('has_batch'),
                    filter_method=self.option("filter_method"),
                    no_filter=True,
                    analysis='split'
                )
        self.tool.set_options(opts)
        self.tool.run()

    def run_uniform(self):
        self.uniform = self.add_tool('denovo_rna_v3.batch.uniform')
        opts = {
            'method': self.option('method'),
            'input_dir': self.tool.output_dir,
        }
        if self.option('method').lower() in ['deseq2', 'degseq', 'edger', 'limma']:
            opts.update({'pvalue_padjust': self.option('pvalue_padjust')})
        self.uniform.set_options(opts)
        self.uniform.on('end', self.set_db)
        self.uniform.run()

    @tryforgood
    def download_s3_file(self, path, to_path):
        """
        判断文件是否在对象存储上
        """
        if not to_path.startswith("/"):
            to_path = os.path.join(self.work_dir, to_path)
        if os.path.exists(to_path):
            os.remove(to_path)
        elif os.path.exists(path):
            to_path = path
        elif exists(path):
            download(path, to_path)
        else:
            self.set_error('file can not find %s', variables=(path), code = '13700502')
        return to_path

    def paste_annotation(self):
        conn = self.all_exp.db['sg_annotation_stat']
        task_id = self.option('task_id')
        try:
            find_result = conn.find_one({"task_id": task_id, "type": "latest", "status": "end"})
        except:
            self.set_error('annotation result file cannot found', code = "13700501")
        else:
            if find_result is None:
                find_result = conn.find_one({"task_id": task_id, "type": "origin"})
        annot = os.path.join(find_result['result_dir'], 'allannot_class/all_annot.xls')
        annot = self.download_s3_file(annot, "annot.xls")
        self.logger.info("annot dir is {}".format(annot))

        exp_level = self.option('exp_level')
        all_annot = pd.read_table(annot, header=0, index_col=0, )
        if exp_level[0].upper() == 'G':
            annot_pd = all_annot[all_annot["is_gene"] == "yes"].drop(columns=['transcript_id', 'is_gene'])
        else:
            annot_pd = all_annot.reset_index().drop(columns=['is_gene']).set_index('transcript_id')
        for each in glob.glob(os.path.join(self.tool.output_dir, '*_vs_*.*.xls')):
            if each.endswith('.annot.xls'):
                continue
            if each.endswith('.normalize.xls'):
                continue
            if each.endswith('.sizeFactor.xls'):
                continue
            if each.endswith('.normFactor.xls'):
                continue
            # diff_pd = pd.read_table(each, header=0, sep='\t', index_col=0)
            # diff_pd = diff_pd.join(annot_pd, how='left')
            # out_diff = each[:-3] + 'annot.xls'
            # diff_pd.to_csv(out_diff, sep='\t', header=True, index=True)
            diff_pd = pd.read_table(each, header=0, sep='\t', index_col="seq_id")
            ctrl, test = os.path.basename(each).split('.{}.xls'.format(self.option("method").lower()))[0].split('_vs_')
            if self.option("method").lower() in ["degseq", "edger", "deseq2", 'limma', 'svaseqlimma']:
                b = pd.DataFrame(diff_pd,columns = ["fc","log2fc", "pvalue", "padjust", "significant", "regulate"])
                c=diff_pd.drop(["fc","log2fc", "pvalue", "padjust", "significant", "regulate"], axis=1)
            else:
                try:
                    b = pd.DataFrame(diff_pd,columns = ['{}_mean'.format(ctrl), '{}_mean'.format(test),"fc","log2fc", "theta", "D", 'prob', "significant", "regulate"])
                    c = diff_pd.drop(['{}_mean'.format(ctrl), '{}_mean'.format(test),"fc","log2fc", "theta", "D", 'prob', "significant", "regulate"], axis=1)
                except:
                    b = pd.DataFrame(diff_pd,columns = ['{}_mean'.format(ctrl), '{}_mean'.format(test),"fc","log2fc", "D", 'prob', "significant", "regulate"])
                    c = diff_pd.drop(['{}_mean'.format(ctrl), '{}_mean'.format(test),"fc","log2fc", "D", 'prob', "significant", "regulate"], axis=1)
            result = pd.concat([b, c], axis=1)
            order = ["nr", "go", "KO_id", "KO_name", "paths", "cog", "cog_description", "pfam", "swissprot"]
            order2 = ["length"]
            pd1 = pd.DataFrame(annot_pd, columns=order)
            pd2 = pd.DataFrame(annot_pd, columns=order2)
            pd3 = pd.concat([pd2, result], axis=1, join_axes=[result.index])
            pd4 = pd.concat([pd3, pd1], axis=1, join_axes=[pd3.index])
            pd4.to_csv(each, sep='\t', header=True, index=True)


import multiprocessing
import os
import pathlib
import platform
import re
import shutil
import sys
from dataclasses import dataclass
from getpass import getpass
from pathlib import Path
from typing import Iterable, List, Optional

from craft_cli import emit
from craft_parts.sources.git_source import GitSource

from snapcraft import errors

@dataclass
class OSPlatform:
    """Platform definition for a given host."""

    system: str
    release: str
    machine: str

    def __str__(self) -> str:
        """Return the string representation of an OSPlatform."""
        return f"{self.system}/{self.release} ({self.machine})"

# architecture translations from the platform syntax to the deb/snap syntax
# These two architecture mappings are almost inverses of each other, except one map is
# not reversible (same value for different keys)
_ARCH_TRANSLATIONS_PLATFORM_TO_DEB = {
    "aarch64": "arm64",
    "armv7l": "armhf",
    "i686": "i386",
    "ppc": "powerpc",
    "ppc64le": "ppc64el",
    "x86_64": "amd64",
    "AMD64": "amd64",  # Windows support
    "s390x": "s390x",
    "riscv64": "riscv64",
}

# architecture translations from the deb/snap syntax to the platform syntax
_ARCH_TRANSLATIONS_DEB_TO_PLATFORM = {
    "arm64": "aarch64",
    "armhf": "armv7l",
    "i386": "i686",
    "powerpc": "ppc",
    "ppc64el": "ppc64le",
    "amd64": "x86_64",
    "s390x": "s390x",
    "riscv64": "riscv64",
}

_32BIT_USERSPACE_ARCHITECTURE = {
    "aarch64": "armv7l",
    "armv8l": "armv7l",
    "ppc64le": "ppc",
    "x86_64": "i686",
}

def get_os_platform(
    filepath=pathlib.Path(  # noqa: B008 Function call in arg defaults
        "/etc/os-release"
    ),
):
    """Determine a system/release combo for an OS using /etc/os-release if available."""
    system = platform.system()
    release = platform.release()
    machine = platform.machine()

    if system == "Linux":
        try:
            with filepath.open("rt", encoding="utf-8") as release_file:
                lines = release_file.readlines()
        except FileNotFoundError:
            emit.debug("Unable to locate 'os-release' file, using default values")
        else:
            os_release = {}
            for line in lines:
                line = line.strip()  # noqa PLW2901
                if not line or line.startswith("#") or "=" not in line:
                    continue
                key, value = line.rstrip().split("=", 1)
                if value[0] == value[-1] and value[0] in ('"', "'"):
                    value = value[1:-1]
                os_release[key] = value
            system = os_release.get("ID", system)
            release = os_release.get("VERSION_ID", release)

    return OSPlatform(system=system, release=release, machine=machine)

def get_host_architecture():
    """Get host architecture in deb format suitable for base definition."""
    os_platform_machine = get_os_platform().machine

    if platform.architecture()[0] == "32bit":
        userspace = _32BIT_USERSPACE_ARCHITECTURE.get(os_platform_machine)
        if userspace:
            os_platform_machine = userspace

    return _ARCH_TRANSLATIONS_PLATFORM_TO_DEB.get(
        os_platform_machine, os_platform_machine
    )

def convert_architecture_deb_to_platform(architecture: str) -> str:
    """Convert an architecture from deb/snap syntax to platform syntax.

    :param architecture: architecture string in debian/snap syntax
    :return: architecture in platform syntax
    :raises InvalidArchitecture: if architecture is not valid
    """
    platform_arch = _ARCH_TRANSLATIONS_DEB_TO_PLATFORM.get(architecture)
    if not platform_arch:
        raise errors.InvalidArchitecture(architecture)

    return platform_arch

def strtobool(value: str) -> bool:
    """Convert a string representation of truth to true (1) or false (0).

    :param value: a True value of 'y', 'yes', 't', 'true', 'on', and '1'
        or a False value of 'n', 'no', 'f', 'false', 'off', and '0'.
    :raises ValueError: if `value` is not a valid boolean value.
    """
    parsed_value = value.lower()

    if parsed_value in ("y", "yes", "t", "true", "on", "1"):
        return True
    if parsed_value in ("n", "no", "f", "false", "off", "0"):
        return False

    raise ValueError(f"Invalid boolean value of {value!r}")

def is_managed_mode() -> bool:
    """Check if snapcraft is running in a managed environment."""
    managed_flag = os.getenv("SNAPCRAFT_MANAGED_MODE", "n")
    return strtobool(managed_flag)

def get_managed_environment_home_path():
    """Path for home when running in managed environment."""
    return pathlib.Path("/root")

def get_managed_environment_project_path():
    """Path for project when running in managed environment."""
    return get_managed_environment_home_path() / "project"

def get_managed_environment_log_path():
    """Path for log when running in managed environment."""
    return pathlib.Path(
        "/tmp/snapcraft.log"  # noqa: S108 Probable insecure use of temp file
    )

def get_managed_environment_snap_channel() -> Optional[str]:
    """User-specified channel to use when installing Snapcraft snap from Snap Store.

    :returns: Channel string if specified, else None.
    """
    return os.getenv("SNAPCRAFT_INSTALL_SNAP_CHANNEL")

def get_effective_base(
    *,
    base: Optional[str],
    build_base: Optional[str],
    project_type: Optional[str],
    name: Optional[str],
) -> Optional[str]:
    """Return the base to use to create the snap.

    Returns build-base if set, but if not, name is returned if the
    snap is of type base. For all other snaps, the base is returned
    as the build-base.
    """
    if build_base is not None:
        return build_base

    return name if project_type == "base" else base

def get_parallel_build_count() -> int:
    """Obtain the number of concurrent jobs to execute.

    Try different strategies to obtain the number of parallel jobs
    to execute. If they fail, assume the safe default of 1. The
    number of concurrent build jobs can be limited by setting the
    environment variable ``SNAPCRAFT_MAX_PARALLEL_BUILD_COUNT``.

    :return: The number of parallel jobs for the current host.
    """
    try:
        build_count = len(os.sched_getaffinity(0))
        emit.debug(f"CPU count (from process affinity): {build_count}")
    except AttributeError:
        # Fall back to multiprocessing.cpu_count()...
        try:
            build_count = multiprocessing.cpu_count()
            emit.debug(f"CPU count (from multiprocessing): {build_count}")
        except NotImplementedError:
            emit.progress(
                "Unable to determine CPU count; disabling parallel builds",
                permanent=True,
            )
            build_count = 1

    max_count_env = os.environ.get("SNAPCRAFT_MAX_PARALLEL_BUILD_COUNT", "")
    try:
        max_count = int(max_count_env)
        if max_count > 0:
            build_count = min(build_count, max_count)
    except ValueError:
        emit.debug(f"Invalid SNAPCRAFT_MAX_PARALLEL_BUILD_COUNT {max_count_env!r}")

    return build_count

def confirm_with_user(prompt_text, default=False) -> bool:
    """Query user for yes/no answer.

    If stdin is not a tty, the default value is returned.

    If user returns an empty answer, the default value is returned.
    returns default value.

    :returns: True if answer starts with [yY], False if answer starts with [nN],
        otherwise the default.
    """
    if is_managed_mode():
        raise RuntimeError("confirmation not yet supported in managed-mode")

    if not sys.stdin.isatty():
        return default

    choices = " [Y/n]: " if default else " [y/N]: "

    with emit.pause():
        reply = str(input(prompt_text + choices)).lower().strip()

    if reply and reply[0] == "y":
        return True

    if reply and reply[0] == "n":
        return False

    return default

def prompt(prompt_text: str, *, hide: bool = False) -> str:
    """Prompt and return the entered string.

    :param prompt_text: string used for the prompt.
    :param hide: hide user input if True.
    """
    if is_managed_mode():
        raise RuntimeError("prompting not yet supported in managed-mode")

    if not sys.stdin.isatty():
        raise errors.SnapcraftError("prompting not possible with no tty")

    if hide:
        method = getpass
    else:
        method = input  # type: ignore

    with emit.pause():
        return str(method(prompt_text))

def humanize_list(
    items: Iterable[str],
    conjunction: str,
    item_format: str = "{!r}",
    sort: bool = True,
) -> str:
    """Format a list into a human-readable string.

    :param items: list to humanize.
    :param conjunction: the conjunction used to join the final element to
                        the rest of the list (e.g. 'and').
    :param item_format: format string to use per item.
    :param sort: if true, sort the list.
    """
    if not items:
        return ""

    quoted_items = [item_format.format(item) for item in items]

    if sort:
        quoted_items = sorted(quoted_items)

    if len(quoted_items) == 1:
        return quoted_items[0]

    humanized = ", ".join(quoted_items[:-1])

    if len(quoted_items) > 2:
        humanized += ","

    return f"{humanized} {conjunction} {quoted_items[-1]}"

def get_common_ld_library_paths(
    prime_dir: Path, arch_triplet: Optional[str]
) -> List[str]:
    """Return common existing PATH entries for a snap.

    :param prime_dir: Path to the prime directory.
    :param arch_triplet: Architecture triplet of target arch. If None, the list of paths
    will not contain architecture-specific paths.

    :returns: List of common library paths in the prime directory that exist.
    """
    paths = [
        prime_dir / "lib",
        prime_dir / "usr" / "lib",
    ]

    if arch_triplet:
        paths.extend(
            [
                prime_dir / "lib" / arch_triplet,
                prime_dir / "usr" / "lib" / arch_triplet,
            ]
        )

    return [str(p) for p in paths if p.exists()]

def get_ld_library_paths(prime_dir: Path, arch_triplet: Optional[str]) -> str:
    """Return a usable in-snap LD_LIBRARY_PATH variable.

    :param prime_dir: Path to the prime directory.
    :param arch_triplet: Architecture triplet of target arch. If None, LD_LIBRARY_PATH
    will not contain architecture-specific paths.

    :returns: The LD_LIBRARY_PATH environment variable to be used for the snap.
    """
    paths = ["${SNAP_LIBRARY_PATH}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"]
    # Add the default LD_LIBRARY_PATH
    paths += get_common_ld_library_paths(prime_dir, arch_triplet)

    ld_library_path = ":".join(paths)

    return re.sub(str(prime_dir), "$SNAP", ld_library_path)

def get_host_tool(command_name: str) -> str:
    """Return the full path of the given host tool.

    :param command_name: the name of the command to resolve a path for.
    :return: Path to command

    :raises SnapcraftError: if command_name was not found.
    """
    tool = shutil.which(command_name)
    if not tool:
        raise errors.SnapcraftError(
            f"A tool snapcraft depends on could not be found: {command_name!r}",
            resolution="Ensure the tool is installed and available, and try again.",
        )
    return tool

def get_snap_tool(command_name: str) -> str:
    """Return the path of a command found in the snap.

    If snapcraft is not running as a snap, shutil.which() is used
    to resolve the command using PATH.

    :param command_name: the name of the command to resolve a path for.
    :return: Path to command

    :raises SnapcraftError: if command_name was not found.
    """
    if os.environ.get("SNAP_NAME") != "snapcraft":
        return get_host_tool(command_name)

    snap_path = os.getenv("SNAP")
    if snap_path is None:
        raise RuntimeError(
            "The SNAP environment variable is not defined, but SNAP_NAME is?"
        )

    command_path = _find_command_path_in_root(snap_path, command_name)

    if command_path is None:
        raise errors.SnapcraftError(
            f"Cannot find snap tool {command_name!r}",
            resolution="Please report this error to the Snapcraft maintainers.",
        )

    return command_path

def _find_command_path_in_root(root: str, command_name: str) -> Optional[str]:
    for bin_directory in (
        "usr/local/sbin",
        "usr/local/bin",
        "usr/sbin",
        "usr/bin",
        "sbin",
        "bin",
    ):
        path = os.path.join(root, bin_directory, command_name)
        if os.path.exists(path):
            return path

    return None

def process_version(version: Optional[str]) -> str:
    """Handle special version strings."""
    if version is None:
        raise ValueError("version cannot be None")

    new_version = version
    if version == "git":
        emit.progress("Determining the version from the project repo (version: git).")
        new_version = GitSource.generate_version()

    if new_version != version:
        emit.progress(f"Version has been set to {new_version!r}", permanent=True)

    return new_version

def is_snapcraft_running_from_snap() -> bool:
    """Check if snapcraft is running from the snap."""
    return os.getenv("SNAP_NAME") == "snapcraft" and os.getenv("SNAP") is not None


# This program mirrors the meteor program in C++
# getting input from a GUI (ref page 542/543)
# This is for the STEM Festival programs

import tkinter
from random import randrange, randint
from PIL import ImageTk, Image
import time
import math
import meteorStemStatus
import meteorStemUtils

class MeteorGUI:
    def __init__(self):
        # create the main window
        self.main_window = tkinter.Tk()
        w, h = self.main_window.winfo_screenwidth(), self.main_window.winfo_screenheight()
        # self.main_window.overrideredirect(1)           # potentially malicious line
        self.main_window.title("Meteor Simulation")
        self.main_window.minsize(width=w, height=h)  # set the window size

        # create frames for widgets....................see page 549
        # set up a frame for each widget to position them correctly
        self.earth_y = 400
        self.earth_x = 1000

        self.top_frame = tkinter.Frame(self.main_window)
        self.heading_frame = tkinter.Frame(self.main_window)  # has the title
        self.canvas_h = 800
        self.canvas_w = 2000
        self.earth_canvas = tkinter.Canvas(self.main_window, width=self.canvas_w, height=self.canvas_h, bg="black")
        self.explosion_image = Image.open('explosion.png')
        self.explosion_photo_image = ImageTk.PhotoImage(self.explosion_image)
        self.earth_image = Image.open('earth.png')
        self.earth_photo_image = ImageTk.PhotoImage(self.earth_image)
        self.meteor_image = Image.open('meteor.png')
        self.meteor_photo_image = ImageTk.PhotoImage(self.meteor_image)
        self.meteor_x = 0
        self.meteor_y = 0
        self.meteor_dx = 1
        self.meteor_dy = 1
        self.meteor_deltaerr = self.earth_y/self.earth_x
        self.meteor_err = self.meteor_deltaerr - 0.5
        self.meteor_drr = 1
        self.meteor_h = 250
        self.meteor_w = 250
        self.rocket_x = 1050
        self.rocket_y = 400
        self.rocket_h = 10
        self.rocket_w = 20
        self.rocket_dy = 5
        self.rocket_dx = 5

        # self.blank_frame1 = tkinter.Frame(self.main_window)
        self.diameter_frame = tkinter.Frame(self.main_window)
        # self.blank_frame2 = tkinter.Frame(self.main_window)
        self.distance_frame = tkinter.Frame(self.main_window)
        # self.blank_frame3 = tkinter.Frame(self.main_window)

        self.bottom_frame = tkinter.Frame(self.main_window)  # bottom frame for buttons

        # create widgets for the top frame
        # self.blank_label1 = tkinter.Label(self.top_frame, text=' ')
        self.heading_label = tkinter.Label(self.heading_frame, \
                                           text='Meteor Defense System Simulation', font=("Helvetica", 18), fg="blue")

        # self.blank_label2 = tkinter.Label(self.blank_frame1, text=' ')
        # self.blank_label3 = tkinter.Label(self.blank_frame2, text=' ')

        # the Enry widgets
        self.diameter_label = tkinter.Label(self.diameter_frame, \
                                            text='Enter the meteor diameter in meters:  ', font=("Helvetica", 10))
        self.diameter_entry = tkinter.Entry(self.diameter_frame, width=20)
        self.diameter_entry.insert(0, 3)

        self.distance_label = tkinter.Label(self.distance_frame, \
                                            text='     Enter the meteor distance from Earth in miles:  ',
                                            font=("Helvetica", 10))

        self.distance_entry = tkinter.Entry(self.distance_frame, width=20)
        self.distance_entry.insert(0, 300)

        # pack (position and order) the top frame's widgets
        # self.blank_label1.pack()
        self.heading_label.pack()
        # self.blank_label2.pack()

        self.diameter_label.pack(side='left')
        self.diameter_entry.pack(side='left')
        # self.blank_label3.pack()

        self.distance_label.pack(side='left')
        self.distance_entry.pack(side='left')

        # create the button widgets and label for the bottom frame
        # self.blank_label4 = tkinter.Label(self.bottom_frame, text=' ')
        self.runSim_button = tkinter.Button(self.bottom_frame, \
                                            state='normal', text='Run Simulation', command=self.processData)
        # self.runSim_button = tkinter.Button(self.bottom_frame, \
        # text='Run Simulation', command=self.processData)
        # self.blank_label5 = tkinter.Label(self.bottom_frame, text=' ')
        self.quit_button = tkinter.Button(self.bottom_frame, text='Quit', \
                                          command=self.main_window.destroy)
        # self.blank_label6 = tkinter.Label(self.bottom_frame, text=' ')

        # pack the blank label and buttons in the bottom frame
        # self.blank_label4.pack()
        self.runSim_button.pack()
        # self.blank_label5.pack()
        self.quit_button.pack()
        # self.blank_label6.pack()

        # pack the frames
        self.top_frame.pack()
        self.heading_frame.pack()
        self.earth_canvas.pack()
        self.earth_canvas_image = self.earth_canvas.create_image(self.earth_x, self.earth_y, image=self.earth_photo_image)
        self.meteor_canvas_image = self.earth_canvas.create_image(int(self.meteor_x), int(self.meteor_y),
                                                                  image=self.meteor_photo_image)
        # self.blank_frame1.pack()
        self.diameter_frame.pack()
        # self.blank_frame2.pack()
        self.distance_frame.pack()
        # self.blank_frame3.pack()
        self.bottom_frame.pack()

        tkinter.mainloop()

    def animate_meteor(self, cancel=False):
        if cancel==False:
            self.draw_one_meteor_frame()
            self.distance = self.distance - (self.meteorSpeed / 60/10)
            self.distance_label.config(text='Distance is now {:d}'.format(int(self.distance)))
            if (int(self.distance) >= 1200 and int(self.distance) <= 1400):
                self.runSim_button.config(background='green', command=self.animate_rocket_success)
            else:
                self.runSim_button.config(background='red',command=self.animate_rocket_fail)
            self.animate_meteor_id = self.main_window.after(50, self.animate_meteor)
        else:
            self.explosion_image = Image.open('explosion.png')
            self.explosion_image = self.explosion_image.resize((self.meteor_w+40, self.meteor_h+40), Image.ANTIALIAS)
            self.explosion_photo_image = ImageTk.PhotoImage(self.explosion_image)
            self.explosion_canvas_image = self.earth_canvas.create_image(int(self.meteor_x), int(self.meteor_y),
                                                                         image=self.explosion_photo_image)
            self.main_window.after_cancel(self.animate_meteor_id)

    def animate_rocket_fail(self, cancel=False):
        self.runSim_button.config(state='disabled')
        self.rocket_h += 2
        self.rocket_w += 2
        self.rocket_x -= 5
        self.rocket_y -= 5
        self.rocket_image = Image.open('rocket.png')
        self.rocket_image = self.rocket_image.resize((self.rocket_h, self.rocket_w), Image.ANTIALIAS)
        self.rocket_photo_image = ImageTk.PhotoImage(self.rocket_image)
        self.rocket_canvas_image = self.earth_canvas.create_image(int(self.rocket_x), int(self.rocket_y),
                                                                  image=self.rocket_photo_image)
        self.animate_rocket_fail_id =self.main_window.after(100, self.animate_rocket_fail)

    def animate_rocket_success(self, cancel=False):
        if self.rocket_h == 10:
            self.runSim_button.config(state='disabled')
        if cancel==False:
            #while abs(self.meteor_x - self.rocket_x) > 10 and abs(self.meteor_y - self.rocket_y) > 10:
            self.rocket_h += 2
            self.rocket_w += 2
            if self.meteor_x > self.rocket_x:
                self.rocket_x += self.rocket_dx
            elif self.meteor_x < self.rocket_x:
                self.rocket_x -= self.rocket_dx
            if self.meteor_y > self.rocket_y:
                self.rocket_y += self.rocket_dy
            elif self.meteor_y < self.rocket_y:
                self.rocket_y -= self.rocket_dy
            if abs(self.meteor_x - self.rocket_x) < 20 and abs(self.meteor_y - self.rocket_y) < 20:
                self.main_window.after_cancel(self.animate_rocket_success_id)
                self.animate_meteor(True)
                return                      # appears to be needed to cancel after loop
            self.rocket_image = Image.open('rocket.png')
            self.rocket_image = self.rocket_image.resize((self.rocket_h, self.rocket_w), Image.ANTIALIAS)
            self.rocket_photo_image = ImageTk.PhotoImage(self.rocket_image)
            self.rocket_canvas_image = self.earth_canvas.create_image(int(self.rocket_x), int(self.rocket_y),
                                                                          image=self.rocket_photo_image)
            self.animate_rocket_success_id =self.main_window.after(50, self.animate_rocket_success)

    def draw_one_meteor_frame(self):
        self.meteor_image = Image.open('meteor.png')
        self.meteor_image = self.meteor_image.resize((self.meteor_h, self.meteor_w), Image.ANTIALIAS)
        self.meteor_photo_image = ImageTk.PhotoImage(self.meteor_image)
        self.meteor_err = self.meteor_err + self.meteor_deltaerr
        self.meteor_canvas_image = self.earth_canvas.create_image(int(self.meteor_x), int(self.meteor_y),
                                                                  image=self.meteor_photo_image)
        self.meteor_x += self.meteor_dx
        if abs(self.meteor_err) >= 0.5:
            self.meteor_y += self.meteor_dy
            self.meteor_w -= 1
            self.meteor_h -= 1
            self.meteor_err -= self.meteor_drr

    # Retrieve the data from the text box and call the function in meteorCalc
    def processData(self):
        # have to calculate distance relative to Earth, add
        self.distance = 1500
        #self.distance = int((self.distance_entry.get())) + self.earth_x
        self.diam = int((self.diameter_entry.get()))
        self.meteorSpeed = float(120 * self.diam)
        self.meteor_y = randint(0,self.canvas_h)
        self.meteor_x = math.floor(math.sqrt(abs(pow(self.canvas_h, 2) - pow(self.meteor_y, 2))))    # Pythagoras's Identity
        self.meteor_deltaerr = (self.earth_y-self.meteor_y)/(self.earth_x-self.meteor_x)
        self.runSim_button.config(state='normal', text='Launch', background='red', command=self.animate_rocket_fail)
        if self.meteor_x > self.earth_x:
            self.meteor_dx = -self.meteor_dx
        #if self.meteor_x == self.earth_x:
        #    self.meteor_dx = 0
        if self.meteor_y > self.earth_y:
            self.meteor_dy = -self.meteor_dy
            self.meteor_drr = -self.meteor_drr
        #if self.meteor_y == self.earth_y:
        #    self.meteor_dy = 0
        if (self.meteor_x > self.canvas_w):
            self.meteor_x = self.canvas_w
        elif (self.meteor_x < 0):
            self.meteor_x = 0
        if(self.meteor_y > self.canvas_h):
            self.meteor_y = self.canvas_h
        elif (self.meteor_y < 0):
            self.meteor_y = 0
        self.animate_meteor()

        self.diam = (float(self.diam))
        #distance = float(distance)
        # = randrange(800)
        # = int(math.sqrt(math.pow(int(distance*20),2)- math.pow(self.meteor_y, 2)))

        badDiam = False
        badDistance = False

        self.diameter_notice_label = tkinter.Label(self.diameter_frame, \
                                                   text='NOTICE Heding into ifs now and diam is: {:d}'.format(diam))
        self.distance_notice_label = tkinter.Label(self.distance_frame, \
                                                   text='NOTICE Heading into ifs now and distance is: {:d}'.format(
                                                       self.distance))
        self.diameter_notice_label.pack()
        self.distance_notice_label.pack()

        # tkinter.messagebox.showinfo('NOTICE', 'Heading into ifs now and diam is: ' + str(diam))
        # tkinter.messagebox.showinfo('NOTICE', 'Heading into ifs now and distance is: ' + str(distance))

        if meteorStemUtils.validInput(diam) == False:
            diam = '3'
            badDiam = True
            self.badDiam_label = tkinter.Label(self.diameter_frame, \
                                               text='Bad Input Provided badDiam now equals ' + str(badDiam))
            self.badDiam_label.pack()
            # tkinter.messagebox.showinfo('Bad Input Provided ', 'badDiam now equals ' + str(badDiam))

        # tkinter.messagebox.showinfo('BACK IN MAIN', 'Checking for valid distance now and badDistance is: ' + str(badDistance))
        self.badDistance_label = tkinter.Label(self.distance_frame, \
                                               text='BACK IN MAIN Checking for valid distance now and badDistance is: ' + str(
                                                   badDistance))
        self.badDistance_label.pack()
        self.badDistance_label.config(text='TEST')  # this is how I'll update a label within a loop
        for i in range(math.ceil(distance * 60 / meteorSpeed)):
            distance = distance - (meteorSpeed / 60/10)
            self.distance_label.config(text='Distance is now ' + str(distance))
            if (distance >= 1200 and distance <= 1400):
                self.runSim_button.config(background='green')
            elif (distance <= 0):
                self.main_window.destroy()
            else:
                self.runSim_button.config(background='red')
            self.main_window.update()
            # self.main_window.after_idle(self.badDistance_label.config(text='Distance is now: '.format(distance)))
            time.sleep(.1)
        if meteorStemUtils.validInput(distance) == False:
            distance = '300'
            badDistance = True
            self.badDistance_label.config(text='Bad Input Provided badDistance now equals ' + str(badDistance))

            # tkinter.messagebox.showinfo('Bad Input Provided ', 'badDistance now equals ' + str(badDistance))

            # if badDiam == True or badDistance == True:
            # tkinter.messagebox.showinfo('Bad Input Provided!', 'Bad input received.\n  Diameter:  "' + str(diam) +
            # '" \n  Distance: "' + str(distance) + '" \n\nRunning with 3 meters at 500 miles.')

        simRunning = False  # inserted for iconify and de-iconify, could use pack_forget, and then pack() again

        # disable the Run Simulation button until it completes
        # self.runSim_button['state']='disabled'

        # iconify the main window
        # self.main_window.iconify()

        meteorStemStatus.runSimulation(diam, distance, simRunning)

        # Enter the tkinter main loop

        tkinter.self.main_window.mainloop()

        # after the sim runs, de iconify
        if simRunning == False:
            self.main_window.deiconify()

    def runSimulation(diam, distance, simRunning):

        simRunning = True  # to let main loop know to deiconify the entry window

        # tkinter.messagebox.showinfo('Now in runSim function and boolean flag is ', str(simRunning))

        # type conversion page 64
        diamFloat = float(diam)
        distanceFloat = float(distance)

        # boolean vars page 111
        meteorInbound = True
        meteorSpeed = float(120 * diamFloat)
        meteorDistance = distanceFloat

        # showinfo page 538, output formatting page 69
        # tkinter.messagebox.showinfo('Now in runSim function', 'The speed is ' + format(meteorSpeed, '.2f'))

        # Generating another window for status output
        # status_window = tkinter.Tk()
        # status_window.title("System Status")
        # status_window.minsize(width=450,height=200)

        # status_window.geometry('600x400+50-100')   # width, ht, 50 pixels in from left, and 100 up from bottom

        # create frames for widgets....................see page 549
        # set up a frame for each widget to position them correctly

        # status_window.top_frame = tkinter.Frame(status_window)
        # status_window.bottom_frame = tkinter.Frame(status_window)

        # create widgets for the top frame, creating stringVars for text updates and output
        status_window.distanceValue = tkinter.StringVar()
        # convert distance to string and store it - page 546
        status_window.distanceValue.set(distanceFloat)

        status_window.blank_label1 = tkinter.Label(status_window.top_frame, text=' ')
        status_window.distance_label = tkinter.Label(status_window.top_frame, \
                                                     text='Meteor Distance = {:d}' + format(distanceFloat))
        status_window.distanceFloatValue = tkinter.Label(status_window.top_frame, \
                                                         textvariable=status_window.distanceValue)  # page 546

        # pack (position and order) the top frame's widgets
        status_window.blank_label1.pack(side='top')
        status_window.distance_label.pack(side='left')
        status_window.distanceFloatValue.pack(side='left')

        # create the button widget and label for the bottom frame
        status_window.blank_labelb = tkinter.Label(status_window.bottom_frame, text=' ')
        status_window.updateSim_button = tkinter.Button(status_window.bottom_frame, \
                                                        text='Update Simulation', command=simUpdate(distance, diam))

        # pack the blank label and buttons in the bottom frame
        status_window.blank_labelb.pack()
        status_window.updateSim_button.pack()

        # pack the frames
        status_window.top_frame.pack()
        status_window.bottom_frame.pack()

        status_window.lift()  # make it on top
        # tkinter.mainloop()

        # loops page 122, boolean vars page 111

        # call back function for the update button

    def simUpdate(distance, diam):
        distanceData = float(distance)
        diamData = float(diam)
        meteorSpeed = float(120 * diamData)
        # tkinter.messagebox.showinfo('In while loop now', 'The distance is ' + format(distanceData, '.2f'))

        # update the status
        while distanceData > 0:
            distanceData = distanceData - (meteorSpeed / 60)
            # tkinter.messagebox.showinfo('In while loop now', 'The distance is ' + format(distanceData, '.2f'))

        simRunning = False

# create an instance of the class
meteor_sim = MeteorGUI()


import logging
import os
import sys
import pandas as pd
if os.path.exists('/groups/pupko/orenavr2/'):
    src_dir = '/groups/pupko/orenavr2/igomeProfilingPipeline/src'
else:
    src_dir = '/Users/Oren/Dropbox/Projects/gershoni/src'
sys.path.insert(0, src_dir)

from auxiliaries.pipeline_auxiliaries import verify_file_is_not_empty, load_table_to_dict

def get_consensus_sequences_from_meme(meme_path):
    result = []
    with open(meme_path) as f:
        for line in f:
            if not line.startswith('MOTIF'):
                continue
            # "MOTIF CLKGASFLAC_17b_clusterRank_0_uniqueMembers_339_clusterSize_2659173.71.faa"
            result.append(line.split()[1].split('_')[0])

    return result

def get_results(path):
    pvalues = []
    hits = []
    with open(path) as f:
        for line in f:
            if line.startswith('##'):
                # "## PSSM_name	p_Value	True_Hits: num_of_hits"
                continue
            # "CLKGASFLAC_17b_clusterRank_0_uniqueMembers_339_clusterSize_2659173.71.faa\t0.01\tTrue_Hits: 118"
            line_tokens = line.split('\t')
            # consensus.append(line_tokens[0].split('_')[0])
            pvalues.append(line_tokens[1])
            hits.append(line_tokens[2].split()[-1])

    return pvalues, hits

def aggregate_pvalues_results(meme_path, scanning_results_dir_path, bc, samplename2biologicalcondition_path,
                              aggregated_pvalues_path, aggregated_hits_path, done_path, argv='no_argv'):

    samplename2biologicalcondition = load_table_to_dict(samplename2biologicalcondition_path,
                                                'Barcode {} belongs to more than one sample_name!!')

    all_consensuses = get_consensus_sequences_from_meme(meme_path)
    pvalues_f = open(aggregated_pvalues_path, 'w')
    hits_f = open(aggregated_hits_path, 'w')

    #header
    pvalues_result = hits_result = f'sample_name,label,{",".join(all_consensuses)}'
    for file_name in sorted(os.listdir(scanning_results_dir_path)):
        if file_name.endswith('100.txt'):
            raise TypeError  # why?
        if file_name.startswith('.'):
            # system file...
            continue

        if file_name.endswith('00.txt'):
            # next sample is starting
            pvalues_f.write(f'{pvalues_result.rstrip(",")}\n')
            hits_f.write(f'{hits_result.rstrip(",")}\n')
            sample_name = file_name.split('_peptides')[0]
            if bc in sample_name:
                label = samplename2biologicalcondition[sample_name]
            else:
                label = 'other'
            pvalues_result = hits_result = f'{sample_name},{label},'

        pvalues, hits = get_results(os.path.join(scanning_results_dir_path, file_name))
        pvalues_result += ','.join(pvalues) + ','
        hits_result += ','.join(hits) + ','

    pvalues_f.write(f'{pvalues_result.rstrip(",")}\n')
    hits_f.write(f'{hits_result.rstrip(",")}\n')

    pvalues_f.close()
    hits_f.close()

    # remove insignificant features:
    df = pd.read_csv(aggregated_pvalues_path)
    # features with at least one significant score, across positive-labeled samples
    positive_class_df = df[df['label'] != 'other']
    significant_features = (positive_class_df.drop(['sample_name', 'label'], axis=1) < 0.05).sum()>0
    mask = pd.concat([pd.Series([True, True], index=['sample_name', 'label']),
                    significant_features])
    df = df.loc[:, mask]
    # df = pd.concat([df.loc[:, ['sample_name', 'label']], df.drop(['sample_name', 'label'], axis=1).loc[:, significant_features]], axis=1)
    df.to_csv(aggregated_pvalues_path.replace('_insignificant', ''), index=False)

    # make sure that there are results and the file is not empty
    verify_file_is_not_empty(aggregated_pvalues_path)
    verify_file_is_not_empty(aggregated_hits_path)

    with open(done_path, 'w') as f:
        f.write(' '.join(argv) + '\n')

if __name__ == '__main__':

    print(f'Starting {sys.argv[0]}. Executed command is:\n{" ".join(sys.argv)}')

    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('meme_path', help='A path to a (MEME) file with the motifs against which the random peptides were scanned (in silico)')
    parser.add_argument('scanning_results_dir_path', help='A path in which each file contains a hits/pvals computation. '
                                                         'These files will be aggregated into one table.')
    parser.add_argument('biological_condition', help='Positive class\' label. All samples of another biological condition will be labeled as "other"')
    parser.add_argument('aggregated_pvalues_path', help='A path to which the Pvalues table will be written to')
    parser.add_argument('aggregated_hits_path', help='A path to which the hits table will be written to')
    parser.add_argument('samplename2biologicalcondition_path', type=str, help='A path to the sample name to biological condition file')
    parser.add_argument('done_file_path', help='A path to a file that signals that the script finished running successfully.')
    parser.add_argument('-v', '--verbose', action='store_true', help='Increase output verbosity')
    args = parser.parse_args()

    if args.verbose:
        logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger('main')

    aggregate_pvalues_results(args.meme_path, args.scanning_results_dir_path,
                              args.biological_condition, args.samplename2biologicalcondition_path,
                              args.aggregated_pvalues_path, args.aggregated_hits_path,
                              args.done_file_path, argv=sys.argv)


from xml.etree import ElementTree as etree

import markdown
from markdown.treeprocessors import Treeprocessor
from wiki.core.markdown import add_to_registry

class ResponsiveTableExtension(markdown.Extension):
    """Wraps all tables with Bootstrap's table-responsive class"""

    def extendMarkdown(self, md):

        add_to_registry(
            md.treeprocessors, "responsivetable", ResponsiveTableTree(md), "_end"
        )

class ResponsiveTableTree(Treeprocessor):
    """
    NOTE: If you allow inputting of raw <table> tags rather than Markdown code
    for tables, this tree processor will not affect the table, as it gets
    stashed and not managed by a Treeprocessor type extension.
    """

    def run(self, root):
        for table_wrapper in list(root.iter("table")):
            table_new = self.create_table_element()
            self.convert_to_wrapper(table_wrapper)
            self.move_children(table_wrapper, table_new)
            table_wrapper.append(table_new)
        return root

    def create_table_element(self):
        """Create table element with text and tail"""
        element = etree.Element("table")
        element.text = "\n"
        element.tail = "\n"
        return element

    def move_children(self, element1, element2):
        """Moves children from element1 to element2"""
        for child in list(element1):
            element2.append(child)
        # reversed is needed to safely remove items while iterating
        for child in reversed(list(element1)):
            element1.remove(child)

    def convert_to_wrapper(self, element):
        element.tag = "div"
        element.set("class", "table-responsive")

def makeExtension(*args, **kwargs):
    """Return an instance of the extension."""
    return ResponsiveTableExtension(*args, **kwargs)


from django.urls import path
from . views import *

urlpatterns = [
    path('login/', loginn, name='login'),
    path('signup/', signup, name='signup'),
    path('', home, name='home'),
    path('alltodo/', alltodo, name='alltodo'),
    path('delete-todo/', delete_todo, name='deletetodo'),
    path('change-status/',
         change_todo, name='changetodo'),
    path('logout/', signout, name='signout'),
    path('graph-info/', graph, name='graph'),
    path('graph-data/', graphdata, name='graphdata'),

]


# USAGE
# python recognize_faces_video.py --encodings encodings.pickle
# python recognize_faces_video.py --encodings encodings.pickle --output output/jurassic_park_trailer_output.avi --display 0

# import the necessary packages
import os
from datetime import date

from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import load_model
from imutils.video import VideoStream
import numpy as np
import argparse
import imutils
import time
import cv2
from face_detection import FaceDetection

import os
from imutils.video import VideoStream
import face_recognition
import argparse
import imutils
import pickle
import time
import cv2
import time
import pandas as pd
from collections import Counter
import numpy as np
import time
# from detect_mask_video import detect_and_predict_mask

# writer = cv2.VideoWriter(args["output"], fourcc, 20,
#     (frame.shape[1], frame.shape[0]), True)

Fcount = 500
 #enter the number of frames you wanna save
vs = cv2.VideoCapture(0)
global gMask
global gBox
ret,frame = vs.read()
# video = cv2.VideoCapture("/dev/video1") # check this and uncommetn if you are using a jetson nano

# We need to check if camera
# is opened previously or not
if (vs.isOpened() == False):
    print("Error reading video file")

# We need to set resolutions.
# so, convert them from float to integer.
frame_width = int(vs.get(3))
frame_height = int(vs.get(4))

size = (frame_width, frame_height)

# Below VideoWriter object will create
# a frame of above defined The output
# is stored in 'filename.avi' file.
result = cv2.VideoWriter('videos/filename.avi',
                         cv2.VideoWriter_fourcc(*'MJPG'),
                         10, size)

details=pd.read_excel('details.xlsx',index_col=0)
atte=pd.read_excel('attendance.xlsx',index_col=0)

def spo2Func():
    final_sig = []

    frameCount = 1

    def dethreding(z):

        return signal.detrend(z)

        # T=len(z)
        # lamba=10
        # I  = np.identity(T)
        # filt = [1,-2,1]* np.ones((1,T-2),dtype=np.int).T
        # D2 = scipy.sparse.spdiags(data.T, (range(0,3)),T-2,T)
        # detrended_sig =
        # return z_stat

    def face_detect_and_thresh(frame):
        # lower = np.array([0, 1,0], dtype = "uint8")
        # upper = np.array([179, 255, 255], dtype = "uint8")
        lower = np.array([0, 58, 50] , dtype="uint8")
        upper = np.array([30, 255, 255], dtype="uint8")
        # for detecting skin lower and upper are used for creating a mask
        fd = FaceDetection()
        frame, face_frame, ROI1, ROI2, status, mask, extra, box = fd.face_detect(frame)
        frame = extra
        # cv2.imshow("face0",extra)
        converted = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        skinMask = cv2.inRange(converted, lower, upper)
        cv2.imshow("face0", converted)
        cv2.imshow("face3", skinMask)
        # apply a series of erosions and dilations to the mask
        # using an elliptical kernel
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))
        skinMask = cv2.erode(skinMask, kernel, iterations=2)
        skinMask = cv2.dilate(skinMask, kernel, iterations=2)
        # blur the mask to help remove noise, then apply the
        # mask to the frame
        skinMask = cv2.GaussianBlur(skinMask, (3, 3), 0)
        skin = cv2.bitwise_and(frame, frame, mask=skinMask)
        cv2.imshow("skin2", skin)
        gMask = skinMask
        global gBox
        gBox = box
        # show the skin in the image along with the mask
        # cv2.imshow("images", np.hstack([frame, skin]))
        # cv2.imshow("mask",np.hstack([mask]))

        for _ in range(np.shape(mask)[0]):
            for j in range(np.shape(mask)[1]):
                if mask[_][j] == 1:
                    mask[_][j] = 1
                else:
                    mask[_][j] = 0
        mask = mask.astype(int)
        return skin, frame, mask

    def spartialAverage(thresh, frame):
        a = list(np.argwhere(maskT == 1))
        # x=[i[0] for i in a]
        # y=[i[1] for i in a]
        # p=[x,y]
        ind_img = (np.vstack((a)))
        sig_fin = np.zeros([np.shape(ind_img)[0], 3])
        test_fin = []
        for i in range(np.shape(ind_img)[0]):
            sig_temp = frame[ind_img[i, 0], ind_img[i, 1], :]
            sig_temp = sig_temp.reshape((1, 3))
            if sig_temp.any() != 0:
                sig_fin = np.concatenate((sig_fin, sig_temp))
        # print(sig_fin)
        for _ in sig_fin:
            if sum(_) > 0:
                # test_fin=np.concatenate((test_fin,_))
                test_fin.append(_)
        # print("min=>")
        a = [item for item in sig_fin if sum(item) > 0]
        # print(min(a, key=sum))
        min_value = sum(min(a, key=sum))
        max_value = sum(max(a, key=sum))
        # print(sum1)
        img_rgb_mean = np.nanmean(test_fin, axis=0)
        print(img_rgb_mean)
        return img_rgb_mean, min_value, max_value

    def MeanRGB(thresh, frame, img_rgb, last_stage, min_value, max_value):
        cv2.imshow("threshh", img_rgb)
        print("==<>>")
        # print(img_rgb)
        # cv2.waitKey()
        # print(img_rgb[0])
        # thresh=thresh.reshape((1,3))
        # img_rgb_mean=np.nanmean(thresh,axis=0)
        a = [item for item in img_rgb[0] if (sum(item) < max_value and sum(item) > 200)]
        # a = filter(lambda (x,y,z) : i+j+k>764 ,frame[0])
        # print(a[1:10])
        # img_temp = [item for item in img_rgb if sum(item)>764]
        # print(frame[0])
        # print(img_temp)
        # print(np.mean(a, axis=(0)))
        if a:
            print("==>")
            # print(a)
            print("==>")
            img_mean = np.mean(a, axis=(0))
            # print(img_mean)

            return img_mean[::-1]
        else:
            return last_stage

    def MeanRGB2(thresh, frame, img_rgb, min_value, max_value):
        cv2.imshow("threshh", img_rgb)
        print("==<>>")
        # print(img_rgb)
        # cv2.waitKey()
        # print(img_rgb[0])
        # thresh=thresh.reshape((1,3))
        # img_rgb_mean=np.nanmean(thresh,axis=0)
        a = [item for item in img_rgb[0] if (sum(item) < max_value and sum(item) > min_value)]
        # a = filter(lambda (x,y,z) : i+j+k>764 ,frame[0])
        # print(a[1:10])
        # img_temp = [item for item in img_rgb if sum(item)>764]
        # print(frame[0])
        # print(img_temp)
        # print(np.mean(a, axis=(0)))

        print("==>")
        # print(a)
        print("==>")
        img_mean = np.mean(a, axis=(0))
        # print(img_mean)

        return img_mean[::-1]

    def MeanRGB(thresh, frame, img_rgb, last_stage, max_value, min_value):
        cv2.imshow("threshh", img_rgb)
        print("==<>>")
        # print(img_rgb)
        # cv2.waitKey()
        # print(img_rgb[0])
        # thresh=thresh.reshape((1,3))
        # img_rgb_mean=np.nanmean(thresh,axis=0)
        a = [item for item in img_rgb[0] if (sum(item) < 764 and sum(item) > 200)]
        # a = filter(lambda (x,y,z) : i+j+k>764 ,frame[0])
        # print(a[1:10])
        # img_temp = [item for item in img_rgb if sum(item)>764]
        # print(frame[0])
        # print(img_temp)
        # print(np.mean(a, axis=(0)))
        if a:
            print("==>")
            # print(a)
            print("==>")
            img_mean = np.mean(a, axis=(0))
            # print(img_mean)

            return img_mean[::-1]
        else:
            return last_stage

    def MeanRGB2(thresh, frame, img_rgb):
        cv2.imshow("threshh", img_rgb)
        print("==<>>")
        # print(img_rgb)
        # cv2.waitKey()
        # print(img_rgb[0])
        # thresh=thresh.reshape((1,3))
        # img_rgb_mean=np.nanmean(thresh,axis=0)
        a = [item for item in img_rgb[0] if (sum(item) < 764 and sum(item) > 200)]
        # 764 being the max value of skin in HSV range

        # 200 being the min value of hsv range

        # a = filter(lambda (x,y,z) : i+j+k>764 ,frame[0])
        # print(a[1:10])
        # img_temp = [item for item in img_rgb if sum(item)>764]
        # print(frame[0])
        # print(img_temp)
        # print(np.mean(a, axis=(0)))

        # print("==>")
        # print(a)
        # print("==>")
        img_mean = np.mean(a, axis=(0))
        # print(img_mean)

        return img_mean[::-1]

    def face_detect_and_thresh2(frame):
        lower = np.array([0, 58, 50] , dtype="uint8")
        upper = np.array([30, 255, 255], dtype="uint8")
        face_frame = frame[gBox[0]:gBox[1], gBox[2]:gBox[3]]
        # cv2.imshow("face_testing",face_frame)
        frame = face_frame
        cv2.imshow("testing", frame)
        converted = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        skinMask = cv2.inRange(converted, lower, upper)
        # apply a series of erosions and dilations to the mask
        # using an elliptical kernel
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))
        skinMask = cv2.erode(skinMask, kernel, iterations=2)
        skinMask = cv2.dilate(skinMask, kernel, iterations=2)
        # blur the mask to help remove noise, then apply the
        # mask to the frame
        skinMask = cv2.GaussianBlur(skinMask, (3, 3), 0)
        skin = cv2.bitwise_and(frame, frame, mask=skinMask)
        cv2.imshow("skin", skin)
        # print(np.mean(skin, axis=(0, 1)))
        # cv2.imshow("face_testing",skin)
        return skin, frame

    def preprocess(z1, z2, detrended_RGB, window_size, size_video, duration, frame):
        temp = (int(size_video/duration))
        f = frame-2

        main_R = []
        main_B = []
        out = []
        for i in range(len(detrended_RGB)-f+1):

            temp_R = z1[i:i+f-1]
            temp_B = z2[i:i+f-1]
            p = [list(a) for a in zip(temp_R, temp_B)]

            out.append(p)
            # if not main_R:
            #     main_R.append(temp_R)
            # else:
            #     main_R=[main_R,temp_R]
            #
            # if not main_B:
            #     main_B.append(temp_B)
            # else:
            #     main_B=[main_B,temp_B]

        # out=[main_R,main_B]
        # print(out[0])
        return out[0]

    def SPooEsitmate(final_sig, video_size, frames, seconds):
        A = 101.6
        B = 5.834

        #  A and B are empirically determined coefficients, Iac and Idc are respectively the amplitudes of the pulsatile (ac) and dc components
        # the determination of these values were done on the basis of the paper published
        z1 = [item[0] for item in final_sig]
        z3 = [item[2] for item in final_sig]
        #The mean average value of Blue and Red  is taken from the set of frames
        SPO_pre = []
        for _ in range(len(z1)):
            SPO_pre.append([z1[_], z3[_]])
        Spo2 = preprocess(z1, z3, SPO_pre, 10, video_size, 10, frames)

        R_temp = [item[0] for item in Spo2]
        DC_R_comp = np.mean(R_temp)
        AC_R_comp = np.std(R_temp)

        I_r = AC_R_comp/DC_R_comp

        B_temp = [item[1] for item in Spo2]
        DC_B_comp = np.mean(B_temp)
        AC_B_comp = np.std(B_temp)

        I_b = AC_B_comp/DC_B_comp
        SpO2_value = (A-B*((I_b*650)/(I_r*950)))
        # Valye 650 and 950 was chosen as the wavelengths of red and near infrared
        return SpO2_value

    # main code begins from here and all the above helper fuctions are called

    # Enter the path of video you wanna give into the code
    cap = cv2.VideoCapture("videos/filename.avi")

    # Calculates the total number of frames present in the video
    length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    fps = cap.get(cv2.CAP_PROP_FPS)      # OpenCV2 version 2 used "CV_CAP_PROP_FPS"
    frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    print(frame_count)
    duration = frame_count/fps
    seconds = duration % 60

    print(length)

    if (cap.isOpened() == False):
        print("Error opening video file")  # Catching anykind of error while opening video

    while(cap.isOpened()):
        ret, frame = cap.read()

        # cv2.waitKey()
        if frameCount == 1:
            # cv2.imshow("testing if flipped",frame)  #just run this if you have issue with inverted frames, can result in fail detection
            # cv2.waitKey()
            # cv2.imshow("testing if flipped2",imutils.rotate_bound(frame, 90))  #just run this if you have issue with inverted frames, can result in fail detection
            # cv2.waitKey()
            # frame= imutils.rotate_bound(frame, 90)
            firstFrame = frame
            # The first frame is sent to the fuction to make a face mask
            thresh, img_rgb, maskT = face_detect_and_thresh(firstFrame)
            cv2.imshow("img_rgb", img_rgb)
            # cv2.waitKey()
            frameCount += 1

        if ret == True:
            # cv2.imshow("testing if flipped",frame)  #just run this if you have issue with inverted frames, can result in fail detection
            frameCount += 1
            # frame= imutils.rotate_bound(frame, 90)

            print(frameCount)
            cv2.imshow('Frame', frame)

            start = time.time()
            # rest of frames are sent to a function to have a threshold/overlap with mask
            thresh, img_rgb = face_detect_and_thresh2(frame)
            end = time.time()
            print(end - start)

            # start = time.time()
            #
            # final_sig.append(spartialAverage(maskT,frame)) # The frames are then sent to spartialAverage to find RGB mean values
            # end = time.time()
            # print(end - start)

            start = time.time()
            if final_sig:
                # The frames are then sent to spartialAverage to find RGB mean values
                final_sig.append(MeanRGB(maskT, frame, img_rgb, final_sig[-1], min_value, max_value))
            else:
                temp, min_value, max_value = spartialAverage(maskT, frame)
                final_sig.append(temp)
            end = time.time()
            print(end - start)

            #  Those rgb values are appened to final_sig which will be used for spo2 estimation
            if cv2.waitKey(25) & 0xFF == ord('q'):  # end the loop if your press 'q' or video reaches end
                break
            # if frameCount==32:
            #     break

      # Break the loop
        else:
            break

    # When everything done, release
    # the video capture object
    cap.release()

    # Closes all the frames
    cv2.destroyAllWindows()

    # For debugging you can try saving the signal as a pickle and run usingPick.py
    pickle.dump(final_sig, open("spo2.p", "wb"))
    print(final_sig)
    # the final signal list is sent to SPooEsitmate function with length of the video
    result = SPooEsitmate(final_sig, length, length, seconds)
    print(result)
    return result

def append_df_to_excel(filename, df, sheet_name='Sheet1', startrow=None,
                       truncate_sheet=False,
                       **to_excel_kwargs):
    """
    Append a DataFrame [df] to existing Excel file [filename]
    into [sheet_name] Sheet.
    If [filename] doesn't exist, then this function will create it.

    Parameters:
      filename : File path or existing ExcelWriter
                 (Example: '/path/to/file.xlsx')
      df : dataframe to save to workbook
      sheet_name : Name of sheet which will contain DataFrame.
                   (default: 'Sheet1')
      startrow : upper left cell row to dump data frame.
                 Per default (startrow=None) calculate the last row
                 in the existing DF and write to the next row...
      truncate_sheet : truncate (remove and recreate) [sheet_name]
                       before writing DataFrame to Excel file
      to_excel_kwargs : arguments which will be passed to `DataFrame.to_excel()`
                        [can be dictionary]

    Returns: None
    """
    from openpyxl import load_workbook

    # ignore [engine] parameter if it was passed
    if 'engine' in to_excel_kwargs:
        to_excel_kwargs.pop('engine')

    writer = pd.ExcelWriter(filename, engine='openpyxl')

    # Python 2.x: define [FileNotFoundError] exception if it doesn't exist
    try:
        FileNotFoundError
    except NameError:
        FileNotFoundError = IOError

    try:
        # try to open an existing workbook
        writer.book = load_workbook(filename)

        # get the last row in the existing Excel sheet
        # if it was not specified explicitly
        if startrow is None and sheet_name in writer.book.sheetnames:
            startrow = writer.book[sheet_name].max_row

        # truncate sheet
        if truncate_sheet and sheet_name in writer.book.sheetnames:
            # index of [sheet_name] sheet
            idx = writer.book.sheetnames.index(sheet_name)
            # remove [sheet_name]
            writer.book.remove(writer.book.worksheets[idx])
            # create an empty sheet [sheet_name] using old index
            writer.book.create_sheet(sheet_name, idx)

        # copy existing sheets
        writer.sheets = {ws.title:ws for ws in writer.book.worksheets}
    except FileNotFoundError:
        # file does not exist yet, we will create it
        pass

    if startrow is None:
        startrow = 0

    # write out the new sheet
    df.to_excel(writer, sheet_name,header=False, startrow=startrow, **to_excel_kwargs)

    # save the workbook
    writer.save()

def add_to_list(str_name, atte, details,t,Spo2Count):
	# atte = atte
	# t = time.localtime()
    current_time = time.strftime("%H:%M:%S", t)
    d = details[details['Name'] == str_name]
    d['Time-Stamp'] = current_time
    d['SpO2_value']=Spo2Count
    a = d.index[0]
    return atte.append(d)

def detect_and_predict_mask(frame, faceNet, maskNet):
	# grab the dimensions of the frame and then construct a blob
	# from it
	(h, w) = frame.shape[:2]
	blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300),
		(104.0, 177.0, 123.0))

	# pass the blob through the network and obtain the face detections
	faceNet.setInput(blob)
	detections = faceNet.forward()

	# initialize our list of faces, their corresponding locations,
	# and the list of predictions from our face mask network
	faces = []
	locs = []
	preds = []

	# loop over the detections
	for i in range(0, detections.shape[2]):
		# extract the confidence (i.e., probability) associated with
		# the detection
		confidence = detections[0, 0, i, 2]

		# filter out weak detections by ensuring the confidence is
		# greater than the minimum confidence
		if confidence > args["confidence"]:
			# compute the (x, y)-coordinates of the bounding box for
			# the object
			box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
			(startX, startY, endX, endY) = box.astype("int")

			# ensure the bounding boxes fall within the dimensions of
			# the frame
			(startX, startY) = (max(0, startX), max(0, startY))
			(endX, endY) = (min(w - 1, endX), min(h - 1, endY))

			# extract the face ROI, convert it from BGR to RGB channel
			# ordering, resize it to 224x224, and preprocess it
			face = frame[startY:endY, startX:endX]
			face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
			face = cv2.resize(face, (224, 224))
			face = img_to_array(face)
			face = preprocess_input(face)

			# add the face and bounding boxes to their respective
			# lists
			faces.append(face)
			locs.append((startX, startY, endX, endY))

	# only make a predictions if at least one face was detected
	if len(faces) > 0:
		# for faster inference we'll make batch predictions on *all*
		# faces at the same time rather than one-by-one predictions
		# in the above `for` loop
		faces = np.array(faces, dtype="float32")
		preds = maskNet.predict(faces, batch_size=32)

	# return a 2-tuple of the face locations and their corresponding
	# locations
	return (locs, preds)

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-e", "--encodings", default="encoding2.pickle",required=True,
	help="path to serialized db of facial encodings")
ap.add_argument("-o", "--output", type=str,
	help="path to output video")
ap.add_argument("-y", "--display", type=int, default=1,
	help="whether or not to display output frame to screen")
ap.add_argument("-d", "--detection-method", type=str, default="hog",
	help="face detection model to use: either `hog` or `cnn`")
ap.add_argument("-f", "--face", type=str,
	default="face_detector",
	help="path to face detector model directory")
ap.add_argument("-m", "--model", type=str,
	default="model/mask_detector.model",
	help="path to trained face mask detector model")
ap.add_argument("-c", "--confidence", type=float, default=0.5,
	help="minimum probability to filter weak detections")
args = vars(ap.parse_args())

# load our serialized face detector model from disk
print("[INFO] loading face detector model...")
prototxtPath = os.path.sep.join([args["face"], "deploy.prototxt"])
weightsPath = os.path.sep.join([args["face"],
	"res10_300x300_ssd_iter_140000.caffemodel"])
faceNet = cv2.dnn.readNet(prototxtPath, weightsPath)

# load the face mask detector model from disk
print("[INFO] loading face mask detector model...")
maskNet = load_model(args["model"])

# initialize the video stream and allow the camera sensor to warm up

# load the known faces and embeddings
print("[INFO] loading encodings...")
data = pickle.loads(open(args["encodings"], "rb").read())

model = load_model(args["model"])

# initialize the video stream and pointer to output video file, then
# allow the camera sensor to warm up
print("[INFO] starting video stream...")
# vs = cv2.VideoCapture(0)
writer = None
time.sleep(2.0)

# loop over frames from the video file stream
while True:
	# grab the frame from the threaded video stream
    ret,frame = vs.read()
    while(Fcount):
        ret, frame = vs.read()

        if ret == True:

    # Write the frame into the
    # file 'filename.avi'
            result.write(frame)
            Fcount=Fcount-1
    # Display the frame
    # saved in the file
            # cv2.imshow('Frame', frame)

    # Press S on keyboard
    # to stop the process

    # Break the loop
        else:
            break

    result.release()

    # convert the input frame from BGR to RGB then resize it to have
	# a width of 750px (to speedup processing)
    rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    rgb = imutils.resize(frame, width=750)
    r = frame.shape[1] / float(rgb.shape[1])

	# detect the (x, y)-coordinates of the bounding boxes
	# corresponding to each face in the input frame, then compute
	# the facial embeddings for each face
    boxes = face_recognition.face_locations(rgb,model=args["detection_method"])
    encodings = face_recognition.face_encodings(rgb, boxes)
    names = []

	# loop over the facial embeddings
    for encoding in encodings:
	    # attempt to match each face in the input image to our known
        # encodings
        matches = face_recognition.compare_faces(data["encodings"],	encoding)
        name = "Unknown"

        # check to see if we have found a match
        if True in matches:
	        # find the indexes of all matched faces then initialize a
            # dictionary to count the total number of times each face
            # was matched
            matchedIdxs = [i for (i, b) in enumerate(matches) if b]
            counts = {}

            # loop over the matched indexes and maintain a count for
            # each recognized face face
            for i in matchedIdxs:
                name = data["names"][i]
                counts[name] = counts.get(name, 0) + 1
            print(counts)
			# determine the recognized face with the largest number
			# of votes (note: in the event of an unlikely tie Python
			# will select first entry in the dictionary)
            name = max(counts, key=counts.get)
            print(name)
		# update the list of names
        names.append(name)

	# loop over the recognized faces
    for ((top, right, bottom, left), name) in zip(boxes, names):
        # rescale the face coordinates
        top = int(top * r)
        right = int(right * r)
        bottom = int(bottom * r)
        left = int(left * r)
        face=frame[top:bottom,left:right]
        face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
        face = cv2.resize(face, (224, 224))
        face = img_to_array(face)
        face = preprocess_input(face)
        face = np.expand_dims(face, axis=0)
        # pass the face through the model to determine if the face
        # has a mask or not
        # draw the predicted face name on the image
        cv2.rectangle(frame, (left, top), (right, bottom),(0, 255, 0), 2)
        y = top - 15 if top - 15 > 15 else top + 15
        cv2.putText(frame, name, (left, y), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 255, 0), 2)
        print(name)
        if counts:
            try:
                if counts[name]>4:
                    while 1:
                        print("in loop")
                        cv2.waitKey(1)
                        ret, frame1 = vs.read()
                        # cv2.imshow("Frame", frame1)
                        face = frame1[top+10:bottom-10, left:right]
                        face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
                        face = cv2.resize(face, (224, 224))
                        face = img_to_array(face)
                        face = preprocess_input(face)
                        face = np.expand_dims(face, axis=0)
                        frame1=cv2.rectangle(frame1,(left,top),(right,bottom),(255,0,0),2)
                        cv2.putText(frame1,"Please wear mask in the bounded box",(10,40),cv2.FONT_HERSHEY_SIMPLEX, 0.7,(0,255,255),2)
						# cv2.putText(output, "OpenCV + Jurassic Park!!!", (10, 25),
						# 			cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
                        if args["display"] > 0:
                            cv2.imshow("Frame", frame1)
                            key = cv2.waitKey(1) & 0xFF
                            # if the `q` key was pressed, break from the loop
                            if key == ord("q"):
                                break
                        (mask, withoutMask) = model.predict(face)[0]
                        if mask>withoutMask:
                            frame1 = vs.read()
                            atte = atte
                            t = time.localtime()
                            current_time = time.strftime("%H_%M_%S", t)
                            current_time=str(current_time)
                            today = date.today()
                            if not(str(today) in os.listdir("output/")):
                                os.mkdir("output/"+str(today))
                            cv2.imwrite("output/"+str(today)+"/"+str(name)+"-"+current_time+"non-mask.bmp", frame)
                            cv2.imwrite("output/"+str(today)+"/"+str(name)+"-"+current_time+"mask.bmp", frame)
                            Spo2Count=spo2Func()
                            print(Spo2Count)
                            print("wearing mask")
                            atte = add_to_list(name, atte, details,t,Spo2Count)
                            xl = pd.ExcelFile('attendance.xlsx')
                            today = date.today()

                            if str(today) in xl.sheet_names:
                                append_df_to_excel('attendance.xlsx', atte, sheet_name=str(today))
                            else:
                                with pd.ExcelWriter('attendance.xlsx', mode='A') as writer:
                                    atte.to_excel(writer, sheet_name=str(today))
                                break
                        else:
                            print("not wearing mask")
                            # cv2.imshow("Frame", frame1)
            except Exception as e:
                print(repr(e))
        t = time.localtime()
        current_time = time.strftime("%H:%M:%S", t)
        print(current_time)
	# loop over the detected face locations and their corresponding
	# locations
	# show the output frame
	# cv2.imshow("Frame", frame)
	# if the video writer is None *AND* we are supposed to write
	# the output video to disk initialize the writer
    if writer is None and args["output"] is not None:
        fourcc = cv2.VideoWriter_fourcc(*"MJPG")
        writer = cv2.VideoWriter(args["output"], fourcc, 20,(frame.shape[1], frame.shape[0]), True)

	# if the writer is not None, write the frame with recognized
	# faces t odisk
	# if writer is not None:
	# 	writer.write(frame)

	# check to see if we are supposed to display the output frame to
	# the screen
    if args["display"] > 0:
        cv2.imshow("Frame", frame)
        key = cv2.waitKey(1) & 0xFF
        # if the `q` key was pressed, break from the loop
        if key == ord("q"):
            break

# do a bit of cleanup
cv2.waitKey(1)
cv2.destroyAllWindows()
for i in range (1,5):
    cv2.waitKey(1)

vs.stop()

# check to see if the video writer point needs to be released
if writer is not None:
    writer.release()