<commit_before>#/*******************************************************************************
# * Copyright (c) 2013 GigaSpaces Technologies Ltd. All rights reserved
# *
# * Licensed under the Apache License, Version 2.0 (the "License");
# * you may not use this file except in compliance with the License.
# * You may obtain a copy of the License at
# *
# *       http://www.apache.org/licenses/LICENSE-2.0
# *
# * Unless required by applicable law or agreed to in writing, software
# * distributed under the License is distributed on an "AS IS" BASIS,
#    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    * See the License for the specific language governing permissions and
#    * limitations under the License.
# *******************************************************************************/

import argparse
from os.path import expanduser
import subprocess
import sys

__author__ = 'elip'

from subprocess import CalledProcessError
from retrying import retry
import timeout_decorator

USER_HOME = expanduser('~')
JAVA_OPTS = "-Xms512m -Xmx1024m -XX:PermSize=128m"

class VagrantLxcBoot:

    def __init__(self, args):
        self.working_dir = args.working_dir
        self.cosmo_version = args.cosmo_version
        self.jar_name = "orchestrator-" + self.cosmo_version + "-all"

    @retry(stop='stop_after_attempt', stop_max_attempt_number=3, wait_fixed=3000)
    def install_fabric(self):
        return_code = subprocess.call(["sudo", "pip", "install", "fabric"])
        if return_code != 0:
            raise CalledProcessError(cmd="pip install fabric", returncode=1, output=sys.stderr)

    def run_fabric(self, command):
        from fabric.api import local as lrun
        lrun(command)

    @retry(stop='stop_after_attempt', stop_max_attempt_number=3, wait_fixed=3000)
    def run_with_retry_and_timeout(self, command):
        try:
            print command
            self.run_with_timeout(command)
        except SystemExit:  # lrun does not throw exception, but just exists the process
            raise CalledProcessError(cmd=command, returncode=1, output=sys.stderr)

    """
    Runs the command with a timeout of 10 minutes.
    Since most command are downloads, we assume that if 10 minutes are not enough
    then something is wrong. so we kill the process and let the retry decorator execute it again.
    """
    @timeout_decorator.timeout(60 * 10)
    def run_with_timeout(self, command):
        self.run_fabric(command)

    def pip(self, package):
        self.run_with_retry_and_timeout(self.sudo("pip install --timeout=120 {0}".format(package)))

    def sudo(self, command):
        return "sudo {0}".format(command)

    def apt_get(self, command):
        self.run_with_retry_and_timeout(self.sudo("apt-get {0}".format(command)))

    def add_apt(self, command):
        self.run_with_retry_and_timeout(self.sudo("add-apt-repository {0}".format(command)))

    def apt_key(self, command):
        self.run_with_retry_and_timeout(self.sudo("apt-key add {0}".format(command)))

    def wget(self, url):
        self.run_with_retry_and_timeout("wget {0} -P {1}/".format(url, self.working_dir))

    def install_rabbitmq(self):
        self.run_fabric(self.sudo("sed -i '2i deb http://www.rabbitmq.com/debian/ testing main' /etc/apt/sources.list"))

        self.wget("http://www.rabbitmq.com/rabbitmq-signing-key-public.asc")
        self.apt_key("rabbitmq-signing-key-public.asc")
        self.apt_get("update")
        self.apt_get("install -q -y erlang-nox")
        self.apt_get("install -y -f ")
        self.apt_get("install -q -y rabbitmq-server")
        self.run_fabric(self.sudo("rabbitmq-plugins enable rabbitmq_management"))
        self.run_fabric(self.sudo("rabbitmq-plugins enable rabbitmq_tracing"))
        self.run_fabric(self.sudo("service rabbitmq-server restart"))
        self.run_fabric(self.sudo("rabbitmqctl trace_on"))

    def install_lxc_docker(self):
        self.apt_get("install -q -y python-software-properties")
        self.apt_get("update -qq")
        self.add_apt("-y ppa:dotcloud/lxc-docker")
        self.apt_get("update -qq")
        self.apt_get("install -q -y lxc-docker")

    def install_kernel(self):
        self.add_apt("-y ppa:ubuntu-x-swat/r-lts-backport")
        self.apt_get("update -qq")
        self.apt_get("install -q -y linux-image-3.8.0-19-generic")

    def install_riemann(self):
        self.wget("http://aphyr.com/riemann/riemann_0.2.2_all.deb")
        self.run_fabric(self.sudo("dpkg -i riemann_0.2.2_all.deb"))

    def install_java(self):
        self.apt_get("install -y openjdk-7-jdk")

    def install_celery(self):
        self.pip("billiard==2.7.3.28")
        self.pip("celery==3.0.19")
        self.pip("python-vagrant")
        self.pip("bernhard")

    def install_vagrant(self):
        self.wget(
            "http://files.vagrantup.com/packages/22b76517d6ccd4ef232a4b4ecbaa276aff8037b8/vagrant_1.2.6_x86_64.deb"
        )
        self.run_fabric(self.sudo("dpkg -i vagrant_1.2.6_x86_64.deb"))
        self.install_vagrant_lxc()
        self.add_lxc_box("precise64", "http://dl.dropbox.com/u/13510779/lxc-precise-amd64-2013-07-12.box")

    def install_vagrant_lxc(self):
        self.run_fabric("vagrant plugin install vagrant-lxc")

    """
    Currently not used. provides some more functionallity between the actual host and the virtual box vagrant guest.
    See http://www.virtualbox.org/manual/ch04.html
    """
    def install_guest_additions(self):
        self.apt_get("install -q -y linux-headers-3.8.0-19-generic dkms")
        self.run_fabric("echo 'Downloading VBox Guest Additions...'")
        self.wget("-q http://dlc.sun.com.edgesuite.net/virtualbox/4.2.12/VBoxGuestAdditions_4.2.12.iso")

        guest_additions_script = """mount -o loop,ro /home/vagrant/VBoxGuestAdditions_4.2.12.iso /mnt
echo yes | /mnt/VBoxLinuxAdditions.run
umount /mnt
rm /root/guest_additions.sh
"""

        self.run_fabric("echo -e '" + guest_additions_script + "' > /root/guest_additions.sh")
        self.run_fabric("chmod 700 /root/guest_additions.sh")
        self.run_fabric(
            "sed -i -E 's#^exit 0#[ -x /root/guest_additions.sh ] \\&\\& /root/guest_additions.sh#' /etc/rc.local"
        )
        self.run_fabric("echo 'Installation of VBox Guest Additions is proceeding in the background.'")
        self.run_fabric("echo '\"vagrant reload\" can be used in about 2 minutes to activate the new guest additions.'")

    def install_cosmo(self):

        run_script = """#!/bin/sh
ARGS=\"$@\"
export VAGRANT_DEFAULT_PROVIDER=lxc
java {0} -jar {1}/cosmo.jar $ARGS
""".format(JAVA_OPTS, self.working_dir)

        get_cosmo = "https://s3.amazonaws.com/cosmo-snapshot-maven-repository/travisci/home/travis/" \
                    ".m2/repository/org/cloudifysource/cosmo/orchestrator/" + self.cosmo_version + "/" + self\
            .jar_name + ".jar"

        self.wget(get_cosmo)

        self.run_fabric("mv {0}/{1}.jar cosmo.jar".format(self.working_dir, self.jar_name))

        script_path = self.working_dir + "/cosmo.sh"
        cosmo_exec = open(script_path, "w")
        cosmo_exec.write(run_script)

        self.run_fabric("chmod +x " + script_path)

        self.run_fabric("echo \"alias cosmo='{0}/cosmo.sh'\" > {1}/.bash_aliases".format(self.working_dir, USER_HOME))

    def add_lxc_box(self, name, url):
        self.run_with_retry_and_timeout(
            "vagrant box add {0} {1}".format(name, url)
        )

    def reboot(self):
        self.run_fabric("shutdown -r +1")

    def bootstrap(self):
        self.install_fabric()
        self.install_lxc_docker()
        self.install_kernel()
        self.install_rabbitmq()
        self.install_riemann()
        self.install_celery()
        self.install_vagrant()
        self.install_java()
        self.install_cosmo()
        print "Manager boot completed"

if __name__ == '__main__':

    parser = argparse.ArgumentParser(
        description='Boots and installs all necessary cosmo management components'
    )
    parser.add_argument(
        '--working_dir',
        help='Working directory for all cosmo installation files',
        default="/home/vagrant/cosmo-work"
    )
    parser.add_argument(
        '--cosmo_version',
        help='Version of cosmo that will be used to deploy the dsl',
        default='0.1-SNAPSHOT'
    )

    vagrant_boot = VagrantLxcBoot(parser.parse_args())
    vagrant_boot.bootstrap()<commit_msg>CLOUDIFY-1975 added undeploy argument to cosmo script which removes created vagrant lxc machines<commit_after>#/*******************************************************************************
# * Copyright (c) 2013 GigaSpaces Technologies Ltd. All rights reserved
# *
# * Licensed under the Apache License, Version 2.0 (the "License");
# * you may not use this file except in compliance with the License.
# * You may obtain a copy of the License at
# *
# *       http://www.apache.org/licenses/LICENSE-2.0
# *
# * Unless required by applicable law or agreed to in writing, software
# * distributed under the License is distributed on an "AS IS" BASIS,
#    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    * See the License for the specific language governing permissions and
#    * limitations under the License.
# *******************************************************************************/

import argparse
from os.path import expanduser
import subprocess
import sys

__author__ = 'elip'

from subprocess import CalledProcessError
from retrying import retry
import timeout_decorator

USER_HOME = expanduser('~')
JAVA_OPTS = "-Xms512m -Xmx1024m -XX:PermSize=128m"

class VagrantLxcBoot:

    def __init__(self, args):
        self.working_dir = args.working_dir
        self.cosmo_version = args.cosmo_version
        self.jar_name = "orchestrator-" + self.cosmo_version + "-all"

    @retry(stop='stop_after_attempt', stop_max_attempt_number=3, wait_fixed=3000)
    def install_fabric(self):
        return_code = subprocess.call(["sudo", "pip", "install", "fabric"])
        if return_code != 0:
            raise CalledProcessError(cmd="pip install fabric", returncode=1, output=sys.stderr)

    def run_fabric(self, command):
        from fabric.api import local as lrun
        lrun(command)

    @retry(stop='stop_after_attempt', stop_max_attempt_number=3, wait_fixed=3000)
    def run_with_retry_and_timeout(self, command):
        try:
            print command
            self.run_with_timeout(command)
        except SystemExit:  # lrun does not throw exception, but just exists the process
            raise CalledProcessError(cmd=command, returncode=1, output=sys.stderr)

    """
    Runs the command with a timeout of 10 minutes.
    Since most command are downloads, we assume that if 10 minutes are not enough
    then something is wrong. so we kill the process and let the retry decorator execute it again.
    """
    @timeout_decorator.timeout(60 * 10)
    def run_with_timeout(self, command):
        self.run_fabric(command)

    def pip(self, package):
        self.run_with_retry_and_timeout(self.sudo("pip install --timeout=120 {0}".format(package)))

    def sudo(self, command):
        return "sudo {0}".format(command)

    def apt_get(self, command):
        self.run_with_retry_and_timeout(self.sudo("apt-get {0}".format(command)))

    def add_apt(self, command):
        self.run_with_retry_and_timeout(self.sudo("add-apt-repository {0}".format(command)))

    def apt_key(self, command):
        self.run_with_retry_and_timeout(self.sudo("apt-key add {0}".format(command)))

    def wget(self, url):
        self.run_with_retry_and_timeout("wget {0} -P {1}/".format(url, self.working_dir))

    def install_rabbitmq(self):
        self.run_fabric(self.sudo("sed -i '2i deb http://www.rabbitmq.com/debian/ testing main' /etc/apt/sources.list"))

        self.wget("http://www.rabbitmq.com/rabbitmq-signing-key-public.asc")
        self.apt_key("rabbitmq-signing-key-public.asc")
        self.apt_get("update")
        self.apt_get("install -q -y erlang-nox")
        self.apt_get("install -y -f ")
        self.apt_get("install -q -y rabbitmq-server")
        self.run_fabric(self.sudo("rabbitmq-plugins enable rabbitmq_management"))
        self.run_fabric(self.sudo("rabbitmq-plugins enable rabbitmq_tracing"))
        self.run_fabric(self.sudo("service rabbitmq-server restart"))
        self.run_fabric(self.sudo("rabbitmqctl trace_on"))

    def install_lxc_docker(self):
        self.apt_get("install -q -y python-software-properties")
        self.apt_get("update -qq")
        self.add_apt("-y ppa:dotcloud/lxc-docker")
        self.apt_get("update -qq")
        self.apt_get("install -q -y lxc-docker")

    def install_kernel(self):
        self.add_apt("-y ppa:ubuntu-x-swat/r-lts-backport")
        self.apt_get("update -qq")
        self.apt_get("install -q -y linux-image-3.8.0-19-generic")

    def install_riemann(self):
        self.wget("http://aphyr.com/riemann/riemann_0.2.2_all.deb")
        self.run_fabric(self.sudo("dpkg -i riemann_0.2.2_all.deb"))

    def install_java(self):
        self.apt_get("install -y openjdk-7-jdk")

    def install_celery(self):
        self.pip("billiard==2.7.3.28")
        self.pip("celery==3.0.19")
        self.pip("python-vagrant")
        self.pip("bernhard")

    def install_vagrant(self):
        self.wget(
            "http://files.vagrantup.com/packages/22b76517d6ccd4ef232a4b4ecbaa276aff8037b8/vagrant_1.2.6_x86_64.deb"
        )
        self.run_fabric(self.sudo("dpkg -i vagrant_1.2.6_x86_64.deb"))
        self.install_vagrant_lxc()
        self.add_lxc_box("precise64", "http://dl.dropbox.com/u/13510779/lxc-precise-amd64-2013-07-12.box")

    def install_vagrant_lxc(self):
        self.run_fabric("vagrant plugin install vagrant-lxc")

    """
    Currently not used. provides some more functionallity between the actual host and the virtual box vagrant guest.
    See http://www.virtualbox.org/manual/ch04.html
    """
    def install_guest_additions(self):
        self.apt_get("install -q -y linux-headers-3.8.0-19-generic dkms")
        self.run_fabric("echo 'Downloading VBox Guest Additions...'")
        self.wget("-q http://dlc.sun.com.edgesuite.net/virtualbox/4.2.12/VBoxGuestAdditions_4.2.12.iso")

        guest_additions_script = """mount -o loop,ro /home/vagrant/VBoxGuestAdditions_4.2.12.iso /mnt
echo yes | /mnt/VBoxLinuxAdditions.run
umount /mnt
rm /root/guest_additions.sh
"""

        self.run_fabric("echo -e '" + guest_additions_script + "' > /root/guest_additions.sh")
        self.run_fabric("chmod 700 /root/guest_additions.sh")
        self.run_fabric(
            "sed -i -E 's#^exit 0#[ -x /root/guest_additions.sh ] \\&\\& /root/guest_additions.sh#' /etc/rc.local"
        )
        self.run_fabric("echo 'Installation of VBox Guest Additions is proceeding in the background.'")
        self.run_fabric("echo '\"vagrant reload\" can be used in about 2 minutes to activate the new guest additions.'")

    def install_cosmo(self):

        run_script = """#!/bin/sh
if [ $# -gt 0 ] && [ "$1" = "undeploy" ]
then
        echo "Undeploying..."
        curdir=`pwd`
        for dir in /tmp/vagrant-vms/*/
        do
                if [ -d "$dir" ]; then
                        cd $dir
                        vagrant destroy -f > /dev/null 2>&1
                fi
        done
        cd $curdir
        rm -rf /tmp/vagrant-vms/*
        echo "done!"
else
        ARGS="$@"
        export VAGRANT_DEFAULT_PROVIDER=lxc
        java -Xms512m -Xmx1024m -XX:PermSize=128m -jar /home/vagrant/cosmo-work/cosmo.jar $ARGS
fi
""".format(JAVA_OPTS, self.working_dir)

        get_cosmo = "https://s3.amazonaws.com/cosmo-snapshot-maven-repository/travisci/home/travis/" \
                    ".m2/repository/org/cloudifysource/cosmo/orchestrator/" + self.cosmo_version + "/" + self\
            .jar_name + ".jar"

        self.wget(get_cosmo)

        self.run_fabric("mv {0}/{1}.jar cosmo.jar".format(self.working_dir, self.jar_name))

        script_path = self.working_dir + "/cosmo.sh"
        cosmo_exec = open(script_path, "w")
        cosmo_exec.write(run_script)

        self.run_fabric("chmod +x " + script_path)

        self.run_fabric("echo \"alias cosmo='{0}/cosmo.sh'\" > {1}/.bash_aliases".format(self.working_dir, USER_HOME))

    def add_lxc_box(self, name, url):
        self.run_with_retry_and_timeout(
            "vagrant box add {0} {1}".format(name, url)
        )

    def reboot(self):
        self.run_fabric("shutdown -r +1")

    def bootstrap(self):
        self.install_fabric()
        self.install_lxc_docker()
        self.install_kernel()
        self.install_rabbitmq()
        self.install_riemann()
        self.install_celery()
        self.install_vagrant()
        self.install_java()
        self.install_cosmo()
        print "Manager boot completed"

if __name__ == '__main__':

    parser = argparse.ArgumentParser(
        description='Boots and installs all necessary cosmo management components'
    )
    parser.add_argument(
        '--working_dir',
        help='Working directory for all cosmo installation files',
        default="/home/vagrant/cosmo-work"
    )
    parser.add_argument(
        '--cosmo_version',
        help='Version of cosmo that will be used to deploy the dsl',
        default='0.1-SNAPSHOT'
    )

    vagrant_boot = VagrantLxcBoot(parser.parse_args())
    vagrant_boot.bootstrap()<|endoftext|>


<commit_before>import argparse
import subprocess
import sys

CLUSTER_SETTINGS = '" -q all.q -V -cwd -e ./log -o ./log {params.sge_opts} -w n -S /bin/bash"'
CLUSTER_FLAG = ("--drmaa", CLUSTER_SETTINGS, "-w", "30")

def _build_prefix(args):
    prefix = ["snakemake", "-pq"]
    if args.distribute:
        prefix.extend(CLUSTER_FLAG)

    return tuple(prefix)

def index(args):
    prefix = _build_prefix(args)
    args = prefix + ("prepare_reference", "--config", "reference=%s" % args.reference)
    return subprocess.call(" ".join(args), shell=True)

def align(args):
    print "Align reads"
    prefix = _build_prefix(args)
    args = prefix + (
        "align_reads",
        "--config",
        "reference=%s" % args.reference,
        "reads=%s" % args.reads,
        "alignments=%s" % args.alignments,
        "alignments_dir=%s" % args.alignments_dir
    )
    return subprocess.call(" ".join(args), shell=True)

def call(args):
    print "Call variants"

def genotype(args):
    print "Genotype"

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--distribute", action="store_true", help="Distribute analysis to Grid Engine-style cluster")
    subparsers = parser.add_subparsers()

    # Index a reference for use by BLASR.
    parser_index = subparsers.add_parser("index", help="index a reference sequence for use by BLASR")
    parser_index.add_argument("reference", help="FASTA file of reference to index")
    parser_index.set_defaults(func=index)

    # Align PacBio reads to an indexed reference with BLASR.
    parser_align = subparsers.add_parser("align", help="align PacBio reads to an indexed reference with BLASR")
    parser_align.add_argument("reference", help="FASTA file of indexed reference with .ctab and .sa in the same directory")
    parser_align.add_argument("reads", help="text file with one absolute path to a PacBio reads file (.bax.h5) per line")
    parser_align.add_argument("--alignments", help="text file with one absolute path to a BLASR alignments file (.bam) per line", default="alignments.fofn")
    parser_align.add_argument("--alignments_dir", help="absolute path of directory for BLASR alignment files", default="alignments")
    parser_align.set_defaults(func=align)

    # Call SVs and indels from BLASR alignments.
    parser_caller = subparsers.add_parser("call", help="call SVs and indels by local assembly of BLASR-aligned reads")
    parser_caller.add_argument("reference", help="FASTA file of indexed reference with .ctab and .sa in the same directory")
    parser_caller.add_argument("alignments", help="text file with one absolute path to a BLASR alignments file (.bam) per line")
    parser_caller.add_argument("variants", help="VCF of variants called by local assembly alignments")
    parser_caller.set_defaults(func=call)

    # Genotype SVs with Illumina reads.
    parser_genotyper = subparsers.add_parser("genotype", help="Genotype SVs with Illumina reads")
    parser_genotyper.add_argument("variants", help="VCF of SMRT SV variants to genotype")
    parser_genotyper.add_argument("genotyped_variants", help="VCF of SMRT SV variant genotypes for the given sample-level BAMs")
    parser_genotyper.add_argument("samples", nargs="+", help="one or more sample-level BAMs to genotype for the given variants")
    parser_genotyper.set_defaults(func=genotype)

    args = parser.parse_args()
    return_code = args.func(args)
    sys.exit(return_code)
<commit_msg>added batches argument to align command.<commit_after>import argparse
import subprocess
import sys

CLUSTER_SETTINGS = '" -q all.q -V -cwd -e ./log -o ./log {params.sge_opts} -w n -S /bin/bash"'
CLUSTER_FLAG = ("--drmaa", CLUSTER_SETTINGS, "-w", "30")

def _build_prefix(args):
    prefix = ["snakemake", "-pq"]
    if args.distribute:
        prefix.extend(CLUSTER_FLAG)

    return tuple(prefix)

def index(args):
    prefix = _build_prefix(args)
    args = prefix + ("prepare_reference", "--config", "reference=%s" % args.reference)
    return subprocess.call(" ".join(args), shell=True)

def align(args):
    print "Align reads"
    prefix = _build_prefix(args)
    args = prefix + (
        "align_reads",
        "--config",
        "reference=%s" % args.reference,
        "reads=%s" % args.reads,
        "alignments=%s" % args.alignments,
        "alignments_dir=%s" % args.alignments_dir,
        "batches=%s" % args.batches
    )
    return subprocess.call(" ".join(args), shell=True)

def call(args):
    print "Call variants"

def genotype(args):
    print "Genotype"

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--distribute", action="store_true", help="Distribute analysis to Grid Engine-style cluster")
    subparsers = parser.add_subparsers()

    # Index a reference for use by BLASR.
    parser_index = subparsers.add_parser("index", help="index a reference sequence for use by BLASR")
    parser_index.add_argument("reference", help="FASTA file of reference to index")
    parser_index.set_defaults(func=index)

    # Align PacBio reads to an indexed reference with BLASR.
    parser_align = subparsers.add_parser("align", help="align PacBio reads to an indexed reference with BLASR")
    parser_align.add_argument("reference", help="FASTA file of indexed reference with .ctab and .sa in the same directory")
    parser_align.add_argument("reads", help="text file with one absolute path to a PacBio reads file (.bax.h5) per line")
    parser_align.add_argument("--alignments", help="text file with one absolute path to a BLASR alignments file (.bam) per line", default="alignments.fofn")
    parser_align.add_argument("--alignments_dir", help="absolute path of directory for BLASR alignment files", default="alignments")
    parser_align.add_argument("--batches", help="number of batches to split input reads into such that there will be one BAM output file per batch", type=int, default=1)
    parser_align.set_defaults(func=align)

    # Call SVs and indels from BLASR alignments.
    parser_caller = subparsers.add_parser("call", help="call SVs and indels by local assembly of BLASR-aligned reads")
    parser_caller.add_argument("reference", help="FASTA file of indexed reference with .ctab and .sa in the same directory")
    parser_caller.add_argument("alignments", help="text file with one absolute path to a BLASR alignments file (.bam) per line")
    parser_caller.add_argument("variants", help="VCF of variants called by local assembly alignments")
    parser_caller.set_defaults(func=call)

    # Genotype SVs with Illumina reads.
    parser_genotyper = subparsers.add_parser("genotype", help="Genotype SVs with Illumina reads")
    parser_genotyper.add_argument("variants", help="VCF of SMRT SV variants to genotype")
    parser_genotyper.add_argument("genotyped_variants", help="VCF of SMRT SV variant genotypes for the given sample-level BAMs")
    parser_genotyper.add_argument("samples", nargs="+", help="one or more sample-level BAMs to genotype for the given variants")
    parser_genotyper.set_defaults(func=genotype)

    args = parser.parse_args()
    return_code = args.func(args)
    sys.exit(return_code)
<|endoftext|>


<commit_before>#!/usr/bin/env python
#
# tournament.py -- implementation of a Swiss-system tournament
#

import psycopg2
import bleach

def connect():
    """

    :rtype: psycopg2.connection
    :return:
    """
    return psycopg2.connect("dbname=tournament")

def deleteMatches(tournament=None):
    """Remove all the match records from the database."""
    conn = connect()
    cur = conn.cursor()
    if tournament:
        cur.execute('SELECT id FROM tournaments where name = (%s);', (tournament,))
        tournament_id = cur.fetchone()[0]
        cur.execute('DELETE FROM matches WHERE t_id = (%s)', (tournament_id,))
    else:
        cur.execute('DELETE FROM matches;')
    conn.commit()
    conn.close()

def deletePlayers():
    """Remove all the player records from the database."""
    conn = connect()
    cur = conn.cursor()
    cur.execute('DELETE FROM players;')
    cur.execute('DELETE FROM tournament_players;')
    cur.execute('DELETE FROM matches;')
    conn.commit()
    conn.close()

def countPlayers():
    """
        Returns the number of players currently registered.
    :return:
    """
    conn = connect()
    cur = conn.cursor()
    cur.execute('SELECT tournament_name, total_players FROM view_tournament_size;')
    tournaments_player_count = cur.fetchall()
    print(tournaments_player_count)
    return tournaments_player_count

def registerTournament(tournament):
    conn = connect()
    cur = conn.cursor()
    cur.execute('INSERT INTO tournaments VALUES (%s) RETURNING id;', (tournament,))
    conn.commit()
    tournament_id = cur.fetchone()[0]
    conn.close()
    return tournament_id

def getTournament(tournament):
    conn = connect()
    cur = conn.cursor()
    cur.execute('SELECT id FROM tournaments WHERE name = %s;', (tournament,))  # TODO: Check the logic on this
    val = cur.fetchone()
    if val:
        tournament_id = val[0]
    else:
        tournament_id = val
    conn.close()
    return tournament_id

def registerPlayer(name, tournament='default'):
    """Adds a player to the tournament database.

    The database assigns a unique serial id number for the player.  (This
    should be handled by your SQL database schema, not in your Python code.)

    Args:
      name: the player's full name (need not be unique).

    :param name:
    :param tournament:
    :return:
    """
    conn = connect()
    cur = conn.cursor()
    cur.execute('INSERT INTO players VALUES (%s) RETURNING id;', (bleach.clean(name),))
    player_id = cur.fetchone()[0]
    tournament_id = getTournament(tournament)
    if not tournament_id:
        tournament_id = registerTournament(tournament)
    print(player_id, tournament_id)
    cur.execute('INSERT INTO tournament_players VALUES (%s, %s);', (str(player_id), str(tournament_id)))
    conn.commit()
    conn.close()

def playerStandings(tournament='default'):
    """Returns a list of the players and their win records, sorted by wins.

    The first entry in the list should be the player in first place, or a player
    tied for first place if there is currently a tie.

    Returns:
      A list of tuples, each of which contains (id, name, wins, matches):
        t_id: The tournament in which the player's participating in
        id: the player's unique id (assigned by the database)
        name: the player's full name (as registered)
        wins: the number of matches the player has won
        matches: the number of matches the player has played
    """

    # TODO: Figure out if tournament is even necessary
    conn = connect()
    cur = conn.cursor()
    tournament_id = getTournament(tournament)
    if not tournament_id:
        return None
    cur.execute('SELECT * FROM view_player_stats WHERE t_id = %s ORDER BY games_won, t_id',
                (tournament_id,))
    standings = cur.fetchall()
    conn.close()
    return standings

def reportMatch(players, tournament='default'):
    """
        Records the outcome of a single match between two players/teams.

    :param players: dictionary of
                    key: id numbers of the players
                    value: Boolean whether they won or lost
    :type players: dict of (str, bool)
    :param tournament:
    :return:
    """
    conn = connect()
    cur = conn.cursor()
    tournament_id = getTournament(tournament)
    cur.execute('SELECT max(match) as last_match FROM matches WHERE t_id = %s;', (tournament_id,))
    last_match = cur.fetchone()[0]
    print(last_match)
    for player in players:
        cur.execute('INSERT INTO matches (t_id, player, winner, match) VALUES (%s, %s, %s, %s);',
                    (tournament_id, player, players[player], last_match+1))
    conn.commit()
    conn.close()

def swissPairings():
    """Returns a list of pairs of players for the next round of a match.

    Assuming that there are an even number of players registered, each player
    appears exactly once in the pairings.  Each player is paired with another
    player with an equal or nearly-equal win record, that is, a player adjacent
    to him or her in the standings.

    Returns:
      A list of tuples, each of which contains (id1, name1, id2, name2)
        id1: the first player's unique id
        name1: the first player's name
        id2: the second player's unique id
        name2: the second player's name
    """

#registerTournament('default')
#print(getTournament('default'))
#print(getTournament('tournament1'))
print(playerStandings('tournament1'))
reportMatch({1:'True', 2:'False'}, tournament='tournament1')
#registerPlayer('Steve Bobs')
#registerPlayer('Michal Frystacky')
#registerPlayer('Steve Davies')
#registerPlayer('test3')
#countPlayers()
#deleteMatches(tournament='tournament1')
#deleteMatches()<commit_msg>Fixed the order in playerStandings()<commit_after>#!/usr/bin/env python
#
# tournament.py -- implementation of a Swiss-system tournament
#

import psycopg2
import bleach

def connect():
    """

    :rtype: psycopg2.connection
    :return:
    """
    return psycopg2.connect("dbname=tournament")

def deleteMatches(tournament=None):
    """Remove all the match records from the database."""
    conn = connect()
    cur = conn.cursor()
    if tournament:
        cur.execute('SELECT id FROM tournaments where name = (%s);', (tournament,))
        tournament_id = cur.fetchone()[0]
        cur.execute('DELETE FROM matches WHERE t_id = (%s)', (tournament_id,))
    else:
        cur.execute('DELETE FROM matches;')
    conn.commit()
    conn.close()

def deletePlayers():
    """Remove all the player records from the database."""
    conn = connect()
    cur = conn.cursor()
    cur.execute('DELETE FROM players;')
    cur.execute('DELETE FROM tournament_players;')
    cur.execute('DELETE FROM matches;')
    conn.commit()
    conn.close()

def countPlayers():
    """
        Returns the number of players currently registered.
    :return:
    """
    conn = connect()
    cur = conn.cursor()
    cur.execute('SELECT tournament_name, total_players FROM view_tournament_size;')
    tournaments_player_count = cur.fetchall()
    print(tournaments_player_count)
    return tournaments_player_count

def registerTournament(tournament):
    conn = connect()
    cur = conn.cursor()
    cur.execute('INSERT INTO tournaments VALUES (%s) RETURNING id;', (tournament,))
    conn.commit()
    tournament_id = cur.fetchone()[0]
    conn.close()
    return tournament_id

def getTournament(tournament):
    conn = connect()
    cur = conn.cursor()
    cur.execute('SELECT id FROM tournaments WHERE name = %s;', (tournament,))  # TODO: Check the logic on this
    val = cur.fetchone()
    if val:
        tournament_id = val[0]
    else:
        tournament_id = val
    conn.close()
    return tournament_id

def registerPlayer(name, tournament='default'):
    """Adds a player to the tournament database.

    The database assigns a unique serial id number for the player.  (This
    should be handled by your SQL database schema, not in your Python code.)

    Args:
      name: the player's full name (need not be unique).

    :param name:
    :param tournament:
    :return:
    """
    conn = connect()
    cur = conn.cursor()
    cur.execute('INSERT INTO players VALUES (%s) RETURNING id;', (bleach.clean(name),))
    player_id = cur.fetchone()[0]
    tournament_id = getTournament(tournament)
    if not tournament_id:
        tournament_id = registerTournament(tournament)
    print(player_id, tournament_id)
    cur.execute('INSERT INTO tournament_players VALUES (%s, %s);', (str(player_id), str(tournament_id)))
    conn.commit()
    conn.close()

def playerStandings(tournament='default'):
    """Returns a list of the players and their win records, sorted by wins.

    The first entry in the list should be the player in first place, or a player
    tied for first place if there is currently a tie.

    Returns:
      A list of tuples, each of which contains (id, name, wins, matches):
        t_id: The tournament in which the player's participating in
        id: the player's unique id (assigned by the database)
        name: the player's full name (as registered)
        wins: the number of matches the player has won
        matches: the number of matches the player has played
    """

    conn = connect()
    cur = conn.cursor()
    tournament_id = getTournament(tournament)
    if not tournament_id:
        return None
    cur.execute('SELECT player, player_name, games_won, games_played FROM view_player_stats WHERE t_id = %s ORDER BY games_won DESC, games_played DESC;',
                (tournament_id,))
    standings = cur.fetchall()
    conn.close()
    return standings

def reportMatch(players, tournament='default'):
    """
        Records the outcome of a single match between two players/teams.

    :param players: dictionary of
                    key: id numbers of the players
                    value: Boolean whether they won or lost
    :type players: dict of (str, bool)
    :param tournament:
    :return:
    """
    conn = connect()
    cur = conn.cursor()
    tournament_id = getTournament(tournament)
    cur.execute('SELECT max(match) as last_match FROM matches WHERE t_id = %s;', (tournament_id,))
    last_match = cur.fetchone()[0]
    print(last_match)
    for player in players:
        cur.execute('INSERT INTO matches (t_id, player, winner, match) VALUES (%s, %s, %s, %s);',
                    (tournament_id, player, players[player], last_match+1))
    conn.commit()
    conn.close()

def swissPairings():
    """Returns a list of pairs of players for the next round of a match.

    Assuming that there are an even number of players registered, each player
    appears exactly once in the pairings.  Each player is paired with another
    player with an equal or nearly-equal win record, that is, a player adjacent
    to him or her in the standings.

    Returns:
      A list of tuples, each of which contains (id1, name1, id2, name2)
        id1: the first player's unique id
        name1: the first player's name
        id2: the second player's unique id
        name2: the second player's name
    """

#registerTournament('default')
#print(getTournament('default'))
#print(getTournament('tournament1'))
print(playerStandings('tournament1'))
#reportMatch({1:'True', 2:'False'}, tournament='tournament1')
#registerPlayer('Steve Bobs')
#registerPlayer('Michal Frystacky')
#registerPlayer('Steve Davies')
#registerPlayer('test3')
#countPlayers()
#deleteMatches(tournament='tournament1')
#deleteMatches()<|endoftext|>


<commit_before>#!/usr/bin/python
#
# Copyright 2014 by Joerg Stephan <jost2208@gmail.com>

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import smtpd
import asyncore
import sqlite3
import time
import pyclamd
from geoip import geolite2

def logit(data):
	# function to log to sqlite db
	#
	try:
		conn = sqlite3.connect('./smsids.db')
		conn.execute(''' INSERT INTO log VALUES (?,?,?,?,?,?,?,?,?,?,?); ''', data)
		conn.commit()
		conn.close()
		return 1
	except:
		print "SQL error", sqlite3.Error
		return 0

def clamalyze(data):
	# function to handle check for malware
	# uses clamd and data as input stream
	try:
		clamd = pyclamd.ClamdAgnostic()
		ret = clamd.scan_stream(data)['stream']
		return ret
	except pyclamd.ConnectionError:
		print 'error connecting to clamd'
	except:
		return ('nope','nope')

class IDS_SMTPServer(smtpd.SMTPServer):

    def process_message(self, peer, mailfrom, rcpttos, data):
	# Creating timestamp now, this represents ID and filename
	Now=time.time()
	filename= "./tmp/"+ str(Now) + ".txt"

	# standard console output
	# includes some basic features which are also written to sqlite
  	print 'ID		      ', Now
	print 'Timestamp	      ', time.strftime('%X %x %Z')
        print 'Receiving message from:', peer[0]
        match = geolite2.lookup(peer[0])
	# GeoIP return must be check against None. Cause local IP addresses would cause an NoneObject Type and crash
	# the script.
	if match is not None:
		Country = match.country
	else:
		Country = 'Unknown'

	print 'Country		: ', Country

	print 'Message addressed from:', mailfrom
        print 'Message addressed to  :', rcpttos
        print 'Message length        :', len(data)
	# clamav scan
	ret = clamalyze(data)
	print 'clamav		     :', ret

	# Open sqlite db file and send data to it
	# Values are like
	# ID - represented via Unix-Timestamp
	# time - via current time
	# IP - the remote IP of the sender, peer[0]
	# port - port used by sender , peer[1]
	# Country Code - via GeoIP match.country
	# mailfrom - the sender email addy
	# rcpttos - to whom the mail was send
	# len(data) - the length (chars) of the origin data msg
	# data	- the data itself
	# filename - file the data was saved to (needed for clamav scan), format always /tmp/ID.txt
	# ClamAv return - what ClamAV has found
	logrow = ( str(Now),time.strftime('%X %x %Z'), peer[0], peer[1], Country, mailfrom,rcpttos[0],str(len(data)),data,filename,ret[1])
	logit(logrow)
	return

print 'Server ready for connections...'
server = IDS_SMTPServer(('0.0.0.0', 25), None)

asyncore.loop()
<commit_msg>v1.0.2 - bugfix<commit_after>#!/usr/bin/python
#
# Copyright 2014 by Joerg Stephan <jost2208@gmail.com>

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import smtpd
import asyncore
import sqlite3
import time
import pyclamd
from geoip import geolite2

def logit(data):
	# function to log to sqlite db
	#
	try:
		conn = sqlite3.connect('./smsids.db')
		conn.execute(''' INSERT INTO log VALUES (?,?,?,?,?,?,?,?,?,?,?); ''', data)
		conn.commit()
		conn.close()
		return 1
	except:
		print "SQL error", sqlite3.Error
		return 0

def clamalyze(data):
	# function to handle check for malware
	# uses clamd and data as input stream
	try:
		clamd = pyclamd.ClamdAgnostic()
		ret = clamd.scan_stream(data)['stream']
		return ret
	except pyclamd.ConnectionError:
		print 'error connecting to clamd'
	except:
		return ('nope','nope')

class IDS_SMTPServer(smtpd.SMTPServer):

    def process_message(self, peer, mailfrom, rcpttos, data):
	# Creating timestamp now, this represents ID and filename
	Now=time.time()
	filename= "./tmp/"+ str(Now) + ".txt"

	# standard console output
	# includes some basic features which are also written to sqlite
  	print 'ID		      ', Now
	print 'Timestamp	      ', time.strftime('%X %x %Z')
        print 'Receiving message from:', peer[0]
        match = geolite2.lookup(peer[0])
	# GeoIP return must be check against None. Cause local IP addresses would cause an NoneObject Type and crash
	# the script.
	if match is not None:
		Country = match.country
	else:
		Country = 'Unknown'

	print 'Country		: ', Country

	print 'Message addressed from:', mailfrom
        print 'Message addressed to  :', rcpttos
        print 'Message length        :', len(data)
	# clamav scan
	ret = clamalyze(data)
	print 'clamav		     :', ret

	# Open sqlite db file and send data to it
	# Values are like
	# ID - represented via Unix-Timestamp
	# time - via current time
	# IP - the remote IP of the sender, peer[0]
	# port - port used by sender , peer[1]
	# Country Code - via GeoIP match.country
	# mailfrom - the sender email addy
	# rcpttos - to whom the mail was send
	# len(data) - the length (chars) of the origin data msg
	# data	- the data itself
	# filename - file the data was saved to (needed for clamav scan), format always /tmp/ID.txt
	# ClamAv return - what ClamAV has found
	logrow = ( str(Now),time.strftime('%X %x %Z'), peer[0], peer[1], Country, mailfrom,rcpttos[0],str(len(data)),data,filename,ret[1])
	print 'Logged?		:', logit(logrow)
	return

print 'Server ready for connections...'
server = IDS_SMTPServer(('0.0.0.0', 25), None)

asyncore.loop()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
""" This file contains some generic purpouse views """

# standard library

# django
from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from django.shortcuts import render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _
from django.views.generic import RedirectView
from django.views.generic import TemplateView
from django.views.generic.detail import DetailView
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.edit import CreateView
from django.views.generic.edit import DeleteView
from django.views.generic.edit import UpdateView
from django.views.generic.list import ListView

from django.views.defaults import bad_request
from django.views.defaults import permission_denied
from django.views.defaults import page_not_found
from django.views.defaults import server_error

# utils
from base.view_utils import clean_query_string
from inflection import underscore

@login_required
def index(request):
    """ view that renders a default home"""
    return render(request, 'index.pug')

def bad_request_view(request, exception, template=None):
    return bad_request(request, exception, 'exceptions/400.pug')

def permission_denied_view(request, exception, template=None):
    return permission_denied(request, exception, 'exceptions/403.pug')

def page_not_found_view(request, exception, template=None):
    return page_not_found(request, exception, 'exceptions/404.pug')

def server_error_view(request, template=None):
    return server_error(request, 'exceptions/500.pug')

class PermissionRequiredMixin:
    permission_required = None

    def check_permission_required(self):
        if self.permission_required:
            if not self.request.user.has_perm(self.permission_required):
                raise PermissionDenied

class BaseDetailView(DetailView, PermissionRequiredMixin):

    def get_title(self):
        verbose_name = self.model._meta.verbose_name
        return '{}: {}'.format(verbose_name, self.object).title()

    def get_context_data(self, **kwargs):
        context = super(BaseDetailView, self).get_context_data(**kwargs)

        context['opts'] = self.model._meta
        context['title'] = self.get_title()

        return context

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseDetailView, self).dispatch(*args, **kwargs)

class BaseCreateView(CreateView, PermissionRequiredMixin):

    def get_context_data(self, **kwargs):
        context = super(BaseCreateView, self).get_context_data(**kwargs)

        verbose_name = self.model._meta.verbose_name
        context['opts'] = self.model._meta
        context['title'] = _('Create %s') % verbose_name
        context['cancel_url'] = self.get_cancel_url()

        return context

    def get_cancel_url(self):
        model_name = self.model.__name__.lower()
        return reverse('{}_list'.format(model_name))

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseCreateView, self).dispatch(*args, **kwargs)

class BaseSubModelCreateView(CreateView, PermissionRequiredMixin):
    """
    Create view when the object is nested within a parent object
    """

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseSubModelCreateView, self).dispatch(*args, **kwargs)

    def get_form_kwargs(self):
        model_underscore_name = underscore(self.parent_model.__name__)

        obj = get_object_or_404(
            self.parent_model,
            pk=self.kwargs['{}_id'.format(model_underscore_name)]
        )

        self.object = self.model(**{model_underscore_name: obj})

        return super(BaseSubModelCreateView, self).get_form_kwargs()

    def get_context_data(self, **kwargs):
        context = super(BaseSubModelCreateView, self).get_context_data(
            **kwargs
        )
        model_underscore_name = underscore(self.parent_model.__name__)

        obj = get_object_or_404(
            self.parent_model,
            pk=self.kwargs['{}_id'.format(model_underscore_name)]
        )

        context[model_underscore_name] = obj
        context['title'] = _('Create %s') % self.model._meta.verbose_name
        context['cancel_url'] = obj.get_absolute_url()

        return context

class BaseListView(ListView, PermissionRequiredMixin):
    paginate_by = 25
    page_kwarg = 'p'
    ignore_kwargs_on_filter = ('q', page_kwarg, 'o')

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseListView, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(BaseListView, self).get_context_data(**kwargs)
        context['opts'] = self.model._meta
        context['clean_query_string'] = clean_query_string(self.request)
        context['q'] = self.request.GET.get('q')
        context['title'] = self.model._meta.verbose_name_plural.title()
        context['ordering'] = self.request.GET.getlist('o')
        return context

    def get_ordering(self):
        """
        Return the field or fields to use for ordering the queryset.
        """
        order = self.request.GET.getlist('o')
        if order:
            return order

        return self.ordering

    def get_queryset(self):
        """
        return the queryset to use on the list and filter by what comes on the
        query string
        """
        queryset = super(BaseListView, self).get_queryset()

        # obtain non ignored kwargs for the filter method
        items = self.request.GET.items()
        params = dict(
            (k, v) for k, v in items if k not in self.ignore_kwargs_on_filter
        )

        # filter
        for key, value in params.items():
            try:
                queryset = queryset.filter(**{key: value})
            except Exception:
                pass
        return queryset

class BaseTemplateView(TemplateView, PermissionRequiredMixin):

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseTemplateView, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(BaseTemplateView, self).get_context_data(**kwargs)

        context['title'] = self.title

        return context

class BaseUpdateView(UpdateView, PermissionRequiredMixin):

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseUpdateView, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(BaseUpdateView, self).get_context_data(**kwargs)

        context['opts'] = self.model._meta
        context['cancel_url'] = self.get_cancel_url()
        context['title'] = _('Update %s') % str(self.object)

        return context

    def get_cancel_url(self):
        return self.object.get_absolute_url()

class BaseDeleteView(DeleteView, PermissionRequiredMixin):
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseDeleteView, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(BaseDeleteView, self).get_context_data(**kwargs)

        context['opts'] = self.model._meta
        context['title'] = _('Delete %s') % str(self.object)

        return context

    def get_success_url(self):
        model_name = self.model.__name__.lower()
        return reverse('{}_list'.format(model_name))

class BaseRedirectView(RedirectView, PermissionRequiredMixin):
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseRedirectView, self).dispatch(*args, **kwargs)

class BaseUpdateRedirectView(
        PermissionRequiredMixin, SingleObjectMixin, RedirectView):

    permanent = False

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super(BaseUpdateRedirectView, self).get(
            request, *args, **kwargs
        )

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.do_action()
        return super(BaseUpdateRedirectView, self).post(
            request, *args, **kwargs
        )

    def do_action():
        """
        Implement this method with the action you want to do before redirect
        """
        pass

    def get_redirect_url(self, *args, **kwargs):
        next_url = self.request.GET.get('next')
        if next_url:
            return next_url

        return self.object.get_absolute_url()

@method_decorator(staff_member_required, name='dispatch')
class StatusView(BaseTemplateView):
    template_name = 'status.pug'
    title = _('status').title()

    def get_context_data(self, **kwargs):
        context = super(StatusView, self).get_context_data(**kwargs)

        context['settings'] = settings

        return context
<commit_msg>Add get_title method to BaseListView<commit_after># -*- coding: utf-8 -*-
""" This file contains some generic purpouse views """

# standard library

# django
from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from django.shortcuts import render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _
from django.views.generic import RedirectView
from django.views.generic import TemplateView
from django.views.generic.detail import DetailView
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.edit import CreateView
from django.views.generic.edit import DeleteView
from django.views.generic.edit import UpdateView
from django.views.generic.list import ListView

from django.views.defaults import bad_request
from django.views.defaults import permission_denied
from django.views.defaults import page_not_found
from django.views.defaults import server_error

# utils
from base.view_utils import clean_query_string
from inflection import underscore

@login_required
def index(request):
    """ view that renders a default home"""
    return render(request, 'index.pug')

def bad_request_view(request, exception, template=None):
    return bad_request(request, exception, 'exceptions/400.pug')

def permission_denied_view(request, exception, template=None):
    return permission_denied(request, exception, 'exceptions/403.pug')

def page_not_found_view(request, exception, template=None):
    return page_not_found(request, exception, 'exceptions/404.pug')

def server_error_view(request, template=None):
    return server_error(request, 'exceptions/500.pug')

class PermissionRequiredMixin:
    permission_required = None

    def check_permission_required(self):
        if self.permission_required:
            if not self.request.user.has_perm(self.permission_required):
                raise PermissionDenied

class BaseDetailView(DetailView, PermissionRequiredMixin):

    def get_title(self):
        verbose_name = self.model._meta.verbose_name
        return '{}: {}'.format(verbose_name, self.object).title()

    def get_context_data(self, **kwargs):
        context = super(BaseDetailView, self).get_context_data(**kwargs)

        context['opts'] = self.model._meta
        context['title'] = self.get_title()

        return context

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseDetailView, self).dispatch(*args, **kwargs)

class BaseCreateView(CreateView, PermissionRequiredMixin):

    def get_context_data(self, **kwargs):
        context = super(BaseCreateView, self).get_context_data(**kwargs)

        verbose_name = self.model._meta.verbose_name
        context['opts'] = self.model._meta
        context['title'] = _('Create %s') % verbose_name
        context['cancel_url'] = self.get_cancel_url()

        return context

    def get_cancel_url(self):
        model_name = self.model.__name__.lower()
        return reverse('{}_list'.format(model_name))

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseCreateView, self).dispatch(*args, **kwargs)

class BaseSubModelCreateView(CreateView, PermissionRequiredMixin):
    """
    Create view when the object is nested within a parent object
    """

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseSubModelCreateView, self).dispatch(*args, **kwargs)

    def get_form_kwargs(self):
        model_underscore_name = underscore(self.parent_model.__name__)

        obj = get_object_or_404(
            self.parent_model,
            pk=self.kwargs['{}_id'.format(model_underscore_name)]
        )

        self.object = self.model(**{model_underscore_name: obj})

        return super(BaseSubModelCreateView, self).get_form_kwargs()

    def get_context_data(self, **kwargs):
        context = super(BaseSubModelCreateView, self).get_context_data(
            **kwargs
        )
        model_underscore_name = underscore(self.parent_model.__name__)

        obj = get_object_or_404(
            self.parent_model,
            pk=self.kwargs['{}_id'.format(model_underscore_name)]
        )

        context[model_underscore_name] = obj
        context['title'] = _('Create %s') % self.model._meta.verbose_name
        context['cancel_url'] = obj.get_absolute_url()

        return context

class BaseListView(ListView, PermissionRequiredMixin):
    paginate_by = 25
    page_kwarg = 'p'
    ignore_kwargs_on_filter = ('q', page_kwarg, 'o')

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseListView, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(BaseListView, self).get_context_data(**kwargs)
        context['opts'] = self.model._meta
        context['clean_query_string'] = clean_query_string(self.request)
        context['q'] = self.request.GET.get('q')
        context['title'] = self.get_title()
        context['ordering'] = self.request.GET.getlist('o')
        return context

    def get_title(self):
        return self.model._meta.verbose_name_plural.title()

    def get_ordering(self):
        """
        Return the field or fields to use for ordering the queryset.
        """
        order = self.request.GET.getlist('o')
        if order:
            return order

        return self.ordering

    def get_queryset(self):
        """
        return the queryset to use on the list and filter by what comes on the
        query string
        """
        queryset = super(BaseListView, self).get_queryset()

        # obtain non ignored kwargs for the filter method
        items = self.request.GET.items()
        params = dict(
            (k, v) for k, v in items if k not in self.ignore_kwargs_on_filter
        )

        # filter
        for key, value in params.items():
            try:
                queryset = queryset.filter(**{key: value})
            except Exception:
                pass
        return queryset

class BaseTemplateView(TemplateView, PermissionRequiredMixin):

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseTemplateView, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(BaseTemplateView, self).get_context_data(**kwargs)

        context['title'] = self.title

        return context

class BaseUpdateView(UpdateView, PermissionRequiredMixin):

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseUpdateView, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(BaseUpdateView, self).get_context_data(**kwargs)

        context['opts'] = self.model._meta
        context['cancel_url'] = self.get_cancel_url()
        context['title'] = _('Update %s') % str(self.object)

        return context

    def get_cancel_url(self):
        return self.object.get_absolute_url()

class BaseDeleteView(DeleteView, PermissionRequiredMixin):
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseDeleteView, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(BaseDeleteView, self).get_context_data(**kwargs)

        context['opts'] = self.model._meta
        context['title'] = _('Delete %s') % str(self.object)

        return context

    def get_success_url(self):
        model_name = self.model.__name__.lower()
        return reverse('{}_list'.format(model_name))

class BaseRedirectView(RedirectView, PermissionRequiredMixin):
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        self.check_permission_required()
        return super(BaseRedirectView, self).dispatch(*args, **kwargs)

class BaseUpdateRedirectView(
        PermissionRequiredMixin, SingleObjectMixin, RedirectView):

    permanent = False

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super(BaseUpdateRedirectView, self).get(
            request, *args, **kwargs
        )

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.do_action()
        return super(BaseUpdateRedirectView, self).post(
            request, *args, **kwargs
        )

    def do_action():
        """
        Implement this method with the action you want to do before redirect
        """
        pass

    def get_redirect_url(self, *args, **kwargs):
        next_url = self.request.GET.get('next')
        if next_url:
            return next_url

        return self.object.get_absolute_url()

@method_decorator(staff_member_required, name='dispatch')
class StatusView(BaseTemplateView):
    template_name = 'status.pug'
    title = _('status').title()

    def get_context_data(self, **kwargs):
        context = super(StatusView, self).get_context_data(**kwargs)

        context['settings'] = settings

        return context
<|endoftext|>


<commit_before>import mido
from Queue import Queue
from threading import Thread
import logging
from datetime import datetime
import os
import music

path = os.path.realpath(__file__)
dir_path = os.path.dirname(os.path.realpath(__file__))

# noinspection PyUnresolvedReferences
input_names = mido.get_output_names()

REFACE = 'reface DX'
MPX = 'MPX16'
SIMPLE_SYNTH = 'SimpleSynth virtual input'

CHANNEL_PARTITION = 8

simple_port = None

key_tracker = music.KeyTracker()

# noinspection PyClassHasNoInit
class InstrumentType:
    """Different types of midi instrument. The program number of each instrument ranges from n * 8 to (n + 1) * 8"""
    piano = 0
    chromatic_percussion = 1
    organ = 2
    guitar = 3
    bass = 4
    strings = 5
    ensemble = 6
    brass = 7
    reed = 8
    pipe = 9
    synth_lead = 10
    synth_pad = 11
    synth_effects = 12
    ethnic = 13
    percussive = 14
    sound_effects = 15

def make_port(name):
    """
    Create a port with the given name, defaulting to the SimpleSynth port otherwise
    :param name: The name of the port
    :return: A port through which midi messages can be sent
    """
    for input_name in input_names:
        if name in input_name:
            name = input_name
    global simple_port
    try:
        # noinspection PyUnresolvedReferences
        return mido.open_output(name)
    except IOError:
        logging.warn("{} not found.".format(name))
        if simple_port is None:
            # noinspection PyUnresolvedReferences
            simple_port = mido.open_output()
        return simple_port

keys_port = make_port(REFACE)
drum_port = make_port(MPX)

class Command:
    """Contains a key value pair that can be passed into a queue to invoke a change in state of a channel"""

    def __init__(self, name, value=None):
        """

        :param name: The name of the command (e.g. command.add_effect)
        :param value: The value of the command (optional)
        """
        self.name = name
        self.value = value

    def __repr__(self):
        return "<Command {} = {}>".format(self.name, self.value)

    def __str__(self):
        return self.__repr__()

    volume = "volume"
    stop = "stop"

class Intervals:
    """Class used to apply set of intervals to a note, using the most likely key"""

    def __init__(self, intervals):
        """

        :param intervals: A series of intervals. For example, [2, 4] would turn a note into a triad
        """
        self.intervals = intervals

    def __call__(self, msg):
        """

        :param msg: A midi note_on message
        :return: An array of midi messages
        """
        new_array = [msg]

        for interval in self.intervals:
            new_msg = msg.copy()
            note = key_tracker.scale.position_at_interval(msg.note, interval)
            if 0 <= note <= 127:
                new_msg.note = note
                new_msg.time = 0
                new_array.append(new_msg)

        return new_array

class Channel(object):
    """Represents an individual midi channel through which messages are passed"""
    note_off = "note_off"
    note_on = "note_on"

    def __init__(self, number, volume=1.0, fade_rate=1, note_on_listener=None):
        """

        :param number: The number of this channel (0-15)
        :param volume: The initial volume of this channel (0.0 - 1.0)
        :param fade_rate: The rate at which this channel should fade
        :param note_on_listener: A listener that is called every time a note_on is played by this channel
        """
        self.note_on_listener = note_on_listener
        self.message_send_listener = None
        self.number = number
        # Decides which port output should be used depending on the channel number
        self.port = keys_port if number < CHANNEL_PARTITION else drum_port
        self.__volume = volume
        self.__volume_queue = Queue()
        self.fade_rate = fade_rate
        self.queue = Queue()
        self.__fade_start = None
        self.__fade_start_queue = Queue()
        self.playing_notes = set()
        self.__intervals = None
        self.__intervals_queue = Queue()
        self.__program = 0

    @property
    def volume(self):
        while not self.__volume_queue.empty():
            self.__volume = self.__volume_queue.get()
        return self.__volume

    @volume.setter
    def volume(self, volume):
        self.fade_start = None
        self.__volume_queue.put(volume)

    @property
    def fade_start(self):
        while not self.__fade_start_queue.empty():
            if self.__fade_start is None:
                self.__fade_start = datetime.now()
        return self.__fade_start

    @fade_start.setter
    def fade_start(self, fade_start):
        self.__fade_start = fade_start

    def fade(self):
        self.__fade_start_queue.put("start")

    @property
    def intervals(self):
        """The set of intervals currently applied to notes played on this channel"""
        while not self.__intervals_queue.empty():
            self.__intervals = self.__intervals_queue.get()
        return self.__intervals

    @intervals.setter
    def intervals(self, intervals):
        self.__intervals_queue.put(intervals)

    @property
    def program(self):
        """The program (i.e. instrument) of this channel"""
        return self.__program

    @program.setter
    def program(self, program):
        self.__program = program
        self.port.send(mido.Message('program_change', program=self.__program, time=0, channel=self.number))

    @property
    def instrument_type(self):
        """The type of instrument (see InstrumentType above)"""
        return self.program / 8

    @instrument_type.setter
    def instrument_type(self, instrument_type):
        if 0 <= instrument_type < 16:
            self.program = 8 * instrument_type + self.instrument_version

    @property
    def instrument_version(self):
        """The version of the instrument. Each instrument has 8 versions."""
        return self.program % 8

    @instrument_version.setter
    def instrument_version(self, instrument_version):
        if 0 <= instrument_version < 8:
            self.program = 8 * self.instrument_type + instrument_version

    def pitch_bend(self, value):
        """
        Send a pitch bend to this channel
        :param value: The value of the pitch bend
        """
        self.port.send(mido.Message('pitchwheel', pitch=value, time=0, channel=self.number))

    def process_waiting_commands(self):
        # Are there any commands waiting?
        while not self.queue.empty():
            command = self.queue.get()
            # Volume changing command. Overrides current fades
            if command.name == Command.volume:
                self.volume = command.value
                self.fade_start = None

    # Send a midi message to this channel
    def send_message(self, msg):

        try:
            # True if a fade out is in progress
            if self.fade_start is not None:
                # How long has the fade been occurring?
                seconds = (datetime.now() - self.fade_start).total_seconds()
                self.volume *= (1 - self.fade_rate * seconds)
                if self.volume < 0:
                    self.volume = 0
                    self.fade_start = None

            if hasattr(msg, 'velocity'):
                msg.velocity = int(self.volume * msg.velocity)

            if hasattr(msg, 'velocity') and self.intervals is not None:
                msgs = self.intervals(msg)
            else:
                msgs = [msg]

            for msg in msgs:
                if callable(self.message_send_listener):
                    self.message_send_listener(msg)
                # Actually send the midi message
                self.port.send(msg)
                if hasattr(msg, "note"):
                    # Update the key tracker
                    key_tracker.add_note(msg.note)
            if hasattr(msg, 'type'):
                # Check if it was a note message
                if msg.type == Channel.note_on:
                    # Keep track of notes that are currently playing
                    self.playing_notes.add(msg.note)
                    if self.note_on_listener is not None:
                        self.note_on_listener(msg)
                elif msg.type == Channel.note_off and msg.note in self.playing_notes:
                    self.playing_notes.remove(msg.note)
        except AttributeError as e:
            logging.exception(e)
        except ValueError as e:
            logging.exception(e)

    # Stop all currently playing notes
    def stop_playing_notes(self):
        for note in self.playing_notes:
            self.stop_note(note)

    # Stop a specific note
    def stop_note(self, note):
        # noinspection PyTypeChecker
        self.send_message(mido.Message(type=Channel.note_off, velocity=0, note=note))

# Represents a midi song loaded from a file
class Song:
    def __init__(self, filename="media/channels_test.mid", is_looping=False):
        self.filename = filename
        self.queue = Queue()
        self.is_stopping = False
        self.is_looping = is_looping
        self.mid = mido.MidiFile("{}/{}".format(dir_path, self.filename))
        self.original_tempo = self.mid.ticks_per_beat
        self.channels = map(Channel, range(0, 16))
        self.__tempo_shift = 1
        self.__tempo_shift_queue = Queue()

    @property
    def tempo_shift(self):
        return self.__tempo_shift

    @tempo_shift.setter
    def tempo_shift(self, tempo_shift):
        self.mid.ticks_per_beat = tempo_shift * self.original_tempo
        self.__tempo_shift = tempo_shift

    def channels_with_instrument_type(self, instrument_type):
        return filter(lambda c: c.instrument_type == instrument_type, self.channels)

    # Play the midi file (should be called on new thread)
    def play_midi_file(self):
        self.is_stopping = False
        play = self.mid.play(meta_messages=True)

        while True:

            for channel in self.channels:
                channel.process_waiting_commands()

            # Take midi messages from a generator
            msg = play.next()

            if msg is None:
                if self.is_looping:
                    play = self.mid.play(meta_messages=True)
                    continue
                break

            # Break if should stop
            if self.is_stopping:
                break
            # Check if any commands in queue
            if not self.queue.empty():
                command = self.queue.get()
                if isinstance(command, Command):
                    if command.name == Command.stop:
                        self.is_stopping = True

            try:
                if isinstance(msg, mido.MetaMessage):
                    continue
                # Send a message to its assigned channel
                self.channels[msg.channel].send_message(msg)
            except AttributeError as e:
                logging.exception(e)
            except IndexError as e:
                logging.exception(e)

    # Start this song on a new thread
    def start(self):
        t = Thread(target=self.play_midi_file)
        t.start()

    # Stop this song
    # noinspection PyUnusedLocal
    def stop(self, *args):
        self.send_command(Command.stop)

    # Queue up a command
    def send_command(self, name, value=None):
        self.queue.put(Command(name, value))

    # Set which channels should be playing
    def set_included_channels(self, pressed_positions):
        for channel in self.channels:
            if channel.number in pressed_positions:
                channel.volume = 1
            else:
                channel.fade()

    # Play all channels
    def include_all_channels(self):
        for channel in self.channels:
            channel.volume = 1
<commit_msg>key tracking note before applying effect<commit_after>import mido
from Queue import Queue
from threading import Thread
import logging
from datetime import datetime
import os
import music

path = os.path.realpath(__file__)
dir_path = os.path.dirname(os.path.realpath(__file__))

# noinspection PyUnresolvedReferences
input_names = mido.get_output_names()

REFACE = 'reface DX'
MPX = 'MPX16'
SIMPLE_SYNTH = 'SimpleSynth virtual input'

CHANNEL_PARTITION = 8

simple_port = None

key_tracker = music.KeyTracker()

# noinspection PyClassHasNoInit
class InstrumentType:
    """Different types of midi instrument. The program number of each instrument ranges from n * 8 to (n + 1) * 8"""
    piano = 0
    chromatic_percussion = 1
    organ = 2
    guitar = 3
    bass = 4
    strings = 5
    ensemble = 6
    brass = 7
    reed = 8
    pipe = 9
    synth_lead = 10
    synth_pad = 11
    synth_effects = 12
    ethnic = 13
    percussive = 14
    sound_effects = 15

def make_port(name):
    """
    Create a port with the given name, defaulting to the SimpleSynth port otherwise
    :param name: The name of the port
    :return: A port through which midi messages can be sent
    """
    for input_name in input_names:
        if name in input_name:
            name = input_name
    global simple_port
    try:
        # noinspection PyUnresolvedReferences
        return mido.open_output(name)
    except IOError:
        logging.warn("{} not found.".format(name))
        if simple_port is None:
            # noinspection PyUnresolvedReferences
            simple_port = mido.open_output()
        return simple_port

keys_port = make_port(REFACE)
drum_port = make_port(MPX)

class Command:
    """Contains a key value pair that can be passed into a queue to invoke a change in state of a channel"""

    def __init__(self, name, value=None):
        """

        :param name: The name of the command (e.g. command.add_effect)
        :param value: The value of the command (optional)
        """
        self.name = name
        self.value = value

    def __repr__(self):
        return "<Command {} = {}>".format(self.name, self.value)

    def __str__(self):
        return self.__repr__()

    volume = "volume"
    stop = "stop"

class Intervals:
    """Class used to apply set of intervals to a note, using the most likely key"""

    def __init__(self, intervals):
        """

        :param intervals: A series of intervals. For example, [2, 4] would turn a note into a triad
        """
        self.intervals = intervals

    def __call__(self, msg):
        """

        :param msg: A midi note_on message
        :return: An array of midi messages
        """
        new_array = [msg]

        for interval in self.intervals:
            new_msg = msg.copy()
            note = key_tracker.scale.position_at_interval(msg.note, interval)
            if 0 <= note <= 127:
                new_msg.note = note
                new_msg.time = 0
                new_array.append(new_msg)

        return new_array

class Channel(object):
    """Represents an individual midi channel through which messages are passed"""
    note_off = "note_off"
    note_on = "note_on"

    def __init__(self, number, volume=1.0, fade_rate=1, note_on_listener=None):
        """

        :param number: The number of this channel (0-15)
        :param volume: The initial volume of this channel (0.0 - 1.0)
        :param fade_rate: The rate at which this channel should fade
        :param note_on_listener: A listener that is called every time a note_on is played by this channel
        """
        self.note_on_listener = note_on_listener
        self.message_send_listener = None
        self.number = number
        # Decides which port output should be used depending on the channel number
        self.port = keys_port if number < CHANNEL_PARTITION else drum_port
        self.__volume = volume
        self.__volume_queue = Queue()
        self.fade_rate = fade_rate
        self.queue = Queue()
        self.__fade_start = None
        self.__fade_start_queue = Queue()
        self.playing_notes = set()
        self.__intervals = None
        self.__intervals_queue = Queue()
        self.__program = 0

    @property
    def volume(self):
        while not self.__volume_queue.empty():
            self.__volume = self.__volume_queue.get()
        return self.__volume

    @volume.setter
    def volume(self, volume):
        self.fade_start = None
        self.__volume_queue.put(volume)

    @property
    def fade_start(self):
        while not self.__fade_start_queue.empty():
            if self.__fade_start is None:
                self.__fade_start = datetime.now()
        return self.__fade_start

    @fade_start.setter
    def fade_start(self, fade_start):
        self.__fade_start = fade_start

    def fade(self):
        self.__fade_start_queue.put("start")

    @property
    def intervals(self):
        """The set of intervals currently applied to notes played on this channel"""
        while not self.__intervals_queue.empty():
            self.__intervals = self.__intervals_queue.get()
        return self.__intervals

    @intervals.setter
    def intervals(self, intervals):
        self.__intervals_queue.put(intervals)

    @property
    def program(self):
        """The program (i.e. instrument) of this channel"""
        return self.__program

    @program.setter
    def program(self, program):
        self.__program = program
        self.port.send(mido.Message('program_change', program=self.__program, time=0, channel=self.number))

    @property
    def instrument_type(self):
        """The type of instrument (see InstrumentType above)"""
        return self.program / 8

    @instrument_type.setter
    def instrument_type(self, instrument_type):
        if 0 <= instrument_type < 16:
            self.program = 8 * instrument_type + self.instrument_version

    @property
    def instrument_version(self):
        """The version of the instrument. Each instrument has 8 versions."""
        return self.program % 8

    @instrument_version.setter
    def instrument_version(self, instrument_version):
        if 0 <= instrument_version < 8:
            self.program = 8 * self.instrument_type + instrument_version

    def pitch_bend(self, value):
        """
        Send a pitch bend to this channel
        :param value: The value of the pitch bend
        """
        self.port.send(mido.Message('pitchwheel', pitch=value, time=0, channel=self.number))

    def process_waiting_commands(self):
        # Are there any commands waiting?
        while not self.queue.empty():
            command = self.queue.get()
            # Volume changing command. Overrides current fades
            if command.name == Command.volume:
                self.volume = command.value
                self.fade_start = None

    # Send a midi message to this channel
    def send_message(self, msg):

        try:
            # True if a fade out is in progress
            if self.fade_start is not None:
                # How long has the fade been occurring?
                seconds = (datetime.now() - self.fade_start).total_seconds()
                self.volume *= (1 - self.fade_rate * seconds)
                if self.volume < 0:
                    self.volume = 0
                    self.fade_start = None

            if hasattr(msg, 'velocity'):
                msg.velocity = int(self.volume * msg.velocity)

            if hasattr(msg, "note"):
                # Update the key tracker
                key_tracker.add_note(msg.note)

            if hasattr(msg, 'velocity') and self.intervals is not None:
                msgs = self.intervals(msg)
            else:
                msgs = [msg]

            for msg in msgs:
                if callable(self.message_send_listener):
                    self.message_send_listener(msg)
                # Actually send the midi message
                self.port.send(msg)
            if hasattr(msg, 'type'):
                # Check if it was a note message
                if msg.type == Channel.note_on:
                    # Keep track of notes that are currently playing
                    self.playing_notes.add(msg.note)
                    if self.note_on_listener is not None:
                        self.note_on_listener(msg)
                elif msg.type == Channel.note_off and msg.note in self.playing_notes:
                    self.playing_notes.remove(msg.note)
        except AttributeError as e:
            logging.exception(e)
        except ValueError as e:
            logging.exception(e)

    # Stop all currently playing notes
    def stop_playing_notes(self):
        for note in self.playing_notes:
            self.stop_note(note)

    # Stop a specific note
    def stop_note(self, note):
        # noinspection PyTypeChecker
        self.send_message(mido.Message(type=Channel.note_off, velocity=0, note=note))

# Represents a midi song loaded from a file
class Song:
    def __init__(self, filename="media/channels_test.mid", is_looping=False):
        self.filename = filename
        self.queue = Queue()
        self.is_stopping = False
        self.is_looping = is_looping
        self.mid = mido.MidiFile("{}/{}".format(dir_path, self.filename))
        self.original_tempo = self.mid.ticks_per_beat
        self.channels = map(Channel, range(0, 16))
        self.__tempo_shift = 1
        self.__tempo_shift_queue = Queue()

    @property
    def tempo_shift(self):
        return self.__tempo_shift

    @tempo_shift.setter
    def tempo_shift(self, tempo_shift):
        self.mid.ticks_per_beat = tempo_shift * self.original_tempo
        self.__tempo_shift = tempo_shift

    def channels_with_instrument_type(self, instrument_type):
        return filter(lambda c: c.instrument_type == instrument_type, self.channels)

    # Play the midi file (should be called on new thread)
    def play_midi_file(self):
        self.is_stopping = False
        play = self.mid.play(meta_messages=True)

        while True:

            for channel in self.channels:
                channel.process_waiting_commands()

            # Take midi messages from a generator
            msg = play.next()

            if msg is None:
                if self.is_looping:
                    play = self.mid.play(meta_messages=True)
                    continue
                break

            # Break if should stop
            if self.is_stopping:
                break
            # Check if any commands in queue
            if not self.queue.empty():
                command = self.queue.get()
                if isinstance(command, Command):
                    if command.name == Command.stop:
                        self.is_stopping = True

            try:
                if isinstance(msg, mido.MetaMessage):
                    continue
                # Send a message to its assigned channel
                self.channels[msg.channel].send_message(msg)
            except AttributeError as e:
                logging.exception(e)
            except IndexError as e:
                logging.exception(e)

    # Start this song on a new thread
    def start(self):
        t = Thread(target=self.play_midi_file)
        t.start()

    # Stop this song
    # noinspection PyUnusedLocal
    def stop(self, *args):
        self.send_command(Command.stop)

    # Queue up a command
    def send_command(self, name, value=None):
        self.queue.put(Command(name, value))

    # Set which channels should be playing
    def set_included_channels(self, pressed_positions):
        for channel in self.channels:
            if channel.number in pressed_positions:
                channel.volume = 1
            else:
                channel.fade()

    # Play all channels
    def include_all_channels(self):
        for channel in self.channels:
            channel.volume = 1
<|endoftext|>