<commit_before>#!/usr/bin/env python
"""Simple performance and scale tests."""

from __future__ import print_function

import argparse
from collections import defaultdict, namedtuple
from datetime import datetime
import logging
import os
import sys
import subprocess
import time

import rrdtool
from jinja2 import Template

from deploy_stack import (
    BootstrapManager,
)
from logbreakdown import (
    _render_ds_string,
    breakdown_log_by_timeframes,
)
import perf_graphing
from utility import (
    add_basic_testing_arguments,
    configure_logging,
    until_timeout,
)

__metaclass__ = type

MINUTE = 60

class TimingData:

    strf_format = '%F %H:%M:%S'

    # Log breakdown uses the start/end too. Perhaps have a property for string
    # rep and a ds for the datetime.
    def __init__(self, start, end):
        self.start = start.strftime(self.strf_format)
        self.end = end.strftime(self.strf_format)
        self.seconds = int((end - start).total_seconds())

DeployDetails = namedtuple(
    'DeployDetails', ['name', 'applications', 'timings'])

SETUP_SCRIPT_PATH = 'perf_static/setup-perf-monitoring.sh'
COLLECTD_CONFIG_PATH = 'perf_static/collectd.conf'

log = logging.getLogger("assess_perf_test_simple")

def assess_perf_test_simple(bs_manager, upload_tools):
    # XXX
    # Find the actual cause for this!! (Something to do with the template and
    # the logs.)
    import sys
    reload(sys)  # NOQA
    sys.setdefaultencoding('utf-8')
    # XXX

    bs_start = datetime.utcnow()
    with bs_manager.booted_context(upload_tools):
        client = bs_manager.client
        admin_client = client.get_controller_client()
        admin_client.wait_for_started()
        bs_end = datetime.utcnow()
        try:
            apply_any_workarounds(client)
            bootstrap_timing = TimingData(bs_start, bs_end)

            setup_system_monitoring(admin_client)

            deploy_details = assess_longrun_perf(
                client, test_length=MINUTE*60*12)
        finally:
            results_dir = os.path.join(
                os.path.abspath(bs_manager.log_dir), 'performance_results/')
            os.makedirs(results_dir)
            admin_client.juju(
                'scp',
                ('--', '-r', '0:/var/lib/collectd/rrd/localhost/*',
                 results_dir)
            )

            csv_results_dir = os.path.join(results_dir, 'csv_results')
            os.makedirs(csv_results_dir)
            admin_client.juju(
                'scp',
                ('--', '-r', '0:/var/lib/collectd/csv/localhost/*',
                 csv_results_dir)
            )

            try:
                admin_client.juju(
                    'scp', ('0:/tmp/mongodb-stats.log', results_dir)
                )
            except subprocess.CalledProcessError as e:
                log.error('Failed to copy mongodb stats: {}'.format(e))
            cleanup_start = datetime.utcnow()
    # Cleanup happens when we move out of context
    cleanup_end = datetime.utcnow()
    cleanup_timing = TimingData(cleanup_start, cleanup_end)
    deployments = dict(
        bootstrap=bootstrap_timing,
        deploys=[deploy_details],
        cleanup=cleanup_timing,
    )

    # Could be smarter about this.
    controller_log_file = os.path.join(
        bs_manager.log_dir,
        'controller',
        'machine-0',
        'machine-0.log.gz')

    generate_reports(controller_log_file, results_dir, deployments)

def apply_any_workarounds(client):
    # Work around mysql charm wanting 80% memory.
    if client.env.get_cloud() == 'lxd':
        constraint_cmd = [
            'lxc',
            'profile',
            'set',
            'juju-{}'.format(client.env.environment),
            'limits.memory',
            '2GB'
        ]
        subprocess.check_output(constraint_cmd)

def generate_reports(controller_log, results_dir, deployments):
    """Generate reports and graphs from run results."""
    cpu_image = generate_cpu_graph_image(results_dir)
    memory_image = generate_memory_graph_image(results_dir)
    network_image = generate_network_graph_image(results_dir)
    mongo_query_image, mongo_memory_image = generate_mongo_graph_image(
        results_dir)

    # Skip doing the logs for such a long run for now.
    # log_message_chunks = get_log_message_in_timed_chunks(
    #     controller_log, deployments)
    log_message_chunks = defaultdict(defaultdict)

    details = dict(
        cpu_graph=cpu_image,
        memory_graph=memory_image,
        network_graph=network_image,
        mongo_graph=mongo_query_image,
        mongo_memory_graph=mongo_memory_image,
        deployments=deployments,
        log_message_chunks=log_message_chunks
    )

    create_html_report(results_dir, details)

def get_log_message_in_timed_chunks(log_file, deployments):
    """Breakdown log into timechunks based on event timeranges in 'deployments'

    """
    deploy_timings = [d.timings for d in deployments['deploys']]

    bootstrap = deployments['bootstrap']
    cleanup = deployments['cleanup']
    all_event_timings = [bootstrap] + deploy_timings + [cleanup]

    raw_details = breakdown_log_by_timeframes(log_file, all_event_timings)

    bs_name = _render_ds_string(bootstrap.start, bootstrap.end)
    cleanup_name = _render_ds_string(cleanup.start, cleanup.end)

    name_lookup = {
        bs_name: 'Bootstrap',
        cleanup_name: 'Kill-Controller',
    }
    for dep in deployments['deploys']:
        name_range = _render_ds_string(
            dep.timings.start, dep.timings.end)
        name_lookup[name_range] = dep.name

    event_details = defaultdict(defaultdict)
    # Outer-layer (i.e. event)
    for event_range in raw_details.keys():
        event_details[event_range]['name'] = name_lookup[event_range]
        event_details[event_range]['logs'] = []

        for log_range in raw_details[event_range].keys():
            timeframe = log_range
            message = '
'.join(raw_details[event_range][log_range])
            event_details[event_range]['logs'].append(
                dict(
                    timeframe=timeframe,
                    message=message))

    return event_details

def create_html_report(results_dir, details):
    # render the html file to the results dir
    with open('./perf_report_template.html', 'rt') as f:
        template = Template(f.read())

    results_output = os.path.join(results_dir, 'report.html')
    with open(results_output, 'wt') as f:
        f.write(template.render(details))

def generate_graph_image(base_dir, results_dir, name, generator):
    metric_files_dir = os.path.join(os.path.abspath(base_dir), results_dir)
    return create_report_graph(metric_files_dir, base_dir, name, generator)

def create_report_graph(rrd_dir, output_dir, name, generator):
    any_file = os.listdir(rrd_dir)[0]
    start, end = get_duration_points(os.path.join(rrd_dir, any_file))
    output_file = os.path.join(
        os.path.abspath(output_dir), '{}.png'.format(name))
    generator(start, end, rrd_dir, output_file)
    print('Created: {}'.format(output_file))
    return output_file

def generate_cpu_graph_image(results_dir):
    return generate_graph_image(
        results_dir, 'aggregation-cpu-average', 'cpu', perf_graphing.cpu_graph)

def generate_memory_graph_image(results_dir):
    return generate_graph_image(
        results_dir, 'memory', 'memory', perf_graphing.memory_graph)

def generate_network_graph_image(results_dir):
    return generate_graph_image(
        results_dir, 'interface-eth0', 'network', perf_graphing.network_graph)

def generate_mongo_graph_image(results_dir):
    dest_path = os.path.join(results_dir, 'mongodb')

    if not perf_graphing.create_mongodb_rrd_file(results_dir, dest_path):
        log.error('Failed to create the MongoDB RRD file.')
        return None
    query_graph = generate_graph_image(
        results_dir, 'mongodb', 'mongodb', perf_graphing.mongodb_graph)

    memory_graph = generate_graph_image(
        results_dir,
        'mongodb',
        'mongodb_memory',
        perf_graphing.mongodb_memory_graph)

    return query_graph, memory_graph

def get_duration_points(rrd_file):
    start = rrdtool.first(rrd_file)
    end = rrdtool.last(rrd_file)

    command = [
        'rrdtool', 'fetch', rrd_file, 'AVERAGE',
        '--start', str(start), '--end', str(end)]
    output = subprocess.check_output(command)

    actual_start = find_actual_start(output)

    return actual_start, end

def find_actual_start(fetch_output):
    # Gets a start timestamp this isn't a NaN.
    for line in fetch_output.splitlines():
        try:
            timestamp, value = line.split(':', 1)
            if not value.startswith(' -nan'):
                return timestamp
        except ValueError:
            pass

class Rest:
    short = MINUTE * 1
    medium = MINUTE * 30
    long = MINUTE * 60
    really_long = MINUTE * 120

def assess_longrun_perf(client, test_length=600):
    longrun_start = datetime.utcnow()
    run_count = 0
    for _ in until_timeout(test_length):
        # Maybe choose one of x different charms/bundles to use.
        bundle_name = 'cs:bundle/wiki-simple-0'
        applications = ['mysql', 'wiki']
        new_client = action_create(client, bundle_name)
        action_busy(new_client, applications)
        action_cleanup(new_client)

        action_rest(Rest.medium)
        run_count += 1

    longrun_end = datetime.utcnow()
    timing_data = TimingData(longrun_start, longrun_end)
    return DeployDetails(
        'Longrun for {}.'.format(test_length/60/60),
        {'Total action runs': run_count},
        timing_data
    )

def action_create(client, bundle):
    # Add a model
    # Deploy something
    # Wait for it to happen
    # Do something else?
    new_model = client.add_model(client.env.clone('newmodel'))
    new_model.deploy(bundle)
    new_model.wait_for_started()
    new_model.wait_for_workloads()

    return new_model

def action_busy(client, applications):
    # Check status (in loop?) <- Busy Stuff
    # Add a unit and wait etc.
    # Further busy stuff
    # Remove unit
    # Busy stuff
    client.get_status()

    for app in applications:
        client.juju('add-unit', (app, '-n', '1'))
        client.wait_for_started()
        client.wait_for_workloads()

    # for _ in until_timeout(MINUTE*10):
    for _ in until_timeout(MINUTE*2):
        log.info('Checking status ping.')
        client.show_status()
        log.info('Sleeping . . .')
        time.sleep(MINUTE)

def action_cleanup(client):
    client.destroy_model()
    action_rest(Rest.short)

def action_rest(rest_length=Rest.short):
    log.info('Resting for {} seconds'.format(rest_length))
    time.sleep(rest_length)

def assess_deployment_perf(client, bundle_name='cs:ubuntu'):
    # This is where multiple services are started either at the same time
    # or one after the other etc.
    deploy_start = datetime.utcnow()

    # We possibly want 2 timing details here, one for started (i.e. agents
    # ready) and the other for the workloads to be complete.
    client.deploy(bundle_name)
    client.wait_for_started()
    client.wait_for_workloads()

    deploy_end = datetime.utcnow()
    deploy_timing = TimingData(deploy_start, deploy_end)

    client_details = get_client_details(client)

    return DeployDetails(bundle_name, client_details, deploy_timing)

def get_client_details(client):
    status = client.get_status()
    units = dict()
    for name in status.get_applications().keys():
        units[name] = status.get_service_unit_count(name)
    return units

def setup_system_monitoring(admin_client):
    # Using ssh get into the machine-0 (or all api/state servers)
    # Install the required packages and start up logging of systems collections
    # and mongodb details.

    installer_script_path = _get_static_script_path(SETUP_SCRIPT_PATH)
    collectd_config_path = _get_static_script_path(COLLECTD_CONFIG_PATH)
    installer_script_dest_path = '/tmp/installer.sh'
    runner_script_dest_path = '/tmp/runner.sh'
    collectd_config_dest_file = '/tmp/collectd.config'

    admin_client.juju(
        'scp',
        (collectd_config_path, '0:{}'.format(collectd_config_dest_file)))

    admin_client.juju(
        'scp',
        (installer_script_path, '0:{}'.format(installer_script_dest_path)))
    admin_client.juju('ssh', ('0', 'chmod +x {}'.format(
        installer_script_dest_path)))
    admin_client.juju(
        'ssh',
        ('0', '{installer} {config_file} {output_file}'.format(
            installer=installer_script_dest_path,
            config_file=collectd_config_dest_file,
            output_file=runner_script_dest_path)))

    # Start collection
    # Respawn incase the initial execution fails for whatever reason.
    admin_client.juju('ssh', ('0', '--', 'daemon --respawn {}'.format(
        runner_script_dest_path)))

def _get_static_script_path(script_path):
    full_path = os.path.abspath(__file__)
    current_dir = os.path.dirname(full_path)
    return os.path.join(current_dir, script_path)

def parse_args(argv):
    """Parse all arguments."""
    parser = argparse.ArgumentParser(description="Simple perf/scale testing")
    add_basic_testing_arguments(parser)
    return parser.parse_args(argv)

def main(argv=None):
    args = parse_args(argv)
    configure_logging(args.verbose)
    bs_manager = BootstrapManager.from_args(args)
    assess_perf_test_simple(bs_manager, args.upload_tools)

    return 0

if __name__ == '__main__':
    sys.exit(main())
<commit_msg>Using dummy charm stack now.<commit_after>#!/usr/bin/env python
"""Simple performance and scale tests."""

from __future__ import print_function

import argparse
from collections import defaultdict, namedtuple
from datetime import datetime
import logging
import os
import sys
import subprocess
import time

import rrdtool
from jinja2 import Template

from deploy_stack import (
    BootstrapManager,
    deploy_dummy_stack,
)
from logbreakdown import (
    _render_ds_string,
    breakdown_log_by_timeframes,
)
import perf_graphing
from utility import (
    add_basic_testing_arguments,
    configure_logging,
    until_timeout,
)

__metaclass__ = type

MINUTE = 60

class TimingData:

    strf_format = '%F %H:%M:%S'

    # Log breakdown uses the start/end too. Perhaps have a property for string
    # rep and a ds for the datetime.
    def __init__(self, start, end):
        self.start = start.strftime(self.strf_format)
        self.end = end.strftime(self.strf_format)
        self.seconds = int((end - start).total_seconds())

DeployDetails = namedtuple(
    'DeployDetails', ['name', 'applications', 'timings'])

SETUP_SCRIPT_PATH = 'perf_static/setup-perf-monitoring.sh'
COLLECTD_CONFIG_PATH = 'perf_static/collectd.conf'

log = logging.getLogger("assess_perf_test_simple")

def assess_perf_test_simple(bs_manager, upload_tools):
    # XXX
    # Find the actual cause for this!! (Something to do with the template and
    # the logs.)
    import sys
    reload(sys)  # NOQA
    sys.setdefaultencoding('utf-8')
    # XXX

    bs_start = datetime.utcnow()
    with bs_manager.booted_context(upload_tools):
        client = bs_manager.client
        admin_client = client.get_controller_client()
        admin_client.wait_for_started()
        bs_end = datetime.utcnow()
        try:
            apply_any_workarounds(client)
            bootstrap_timing = TimingData(bs_start, bs_end)

            setup_system_monitoring(admin_client)

            deploy_details = assess_longrun_perf(
                client, test_length=MINUTE*60*12)
        finally:
            results_dir = os.path.join(
                os.path.abspath(bs_manager.log_dir), 'performance_results/')
            os.makedirs(results_dir)
            admin_client.juju(
                'scp',
                ('--', '-r', '0:/var/lib/collectd/rrd/localhost/*',
                 results_dir)
            )

            csv_results_dir = os.path.join(results_dir, 'csv_results')
            os.makedirs(csv_results_dir)
            admin_client.juju(
                'scp',
                ('--', '-r', '0:/var/lib/collectd/csv/localhost/*',
                 csv_results_dir)
            )

            try:
                admin_client.juju(
                    'scp', ('0:/tmp/mongodb-stats.log', results_dir)
                )
            except subprocess.CalledProcessError as e:
                log.error('Failed to copy mongodb stats: {}'.format(e))
            cleanup_start = datetime.utcnow()
    # Cleanup happens when we move out of context
    cleanup_end = datetime.utcnow()
    cleanup_timing = TimingData(cleanup_start, cleanup_end)
    deployments = dict(
        bootstrap=bootstrap_timing,
        deploys=[deploy_details],
        cleanup=cleanup_timing,
    )

    # Could be smarter about this.
    controller_log_file = os.path.join(
        bs_manager.log_dir,
        'controller',
        'machine-0',
        'machine-0.log.gz')

    generate_reports(controller_log_file, results_dir, deployments)

def apply_any_workarounds(client):
    # Work around mysql charm wanting 80% memory.
    if client.env.get_cloud() == 'lxd':
        constraint_cmd = [
            'lxc',
            'profile',
            'set',
            'juju-{}'.format(client.env.environment),
            'limits.memory',
            '2GB'
        ]
        subprocess.check_output(constraint_cmd)

def generate_reports(controller_log, results_dir, deployments):
    """Generate reports and graphs from run results."""
    cpu_image = generate_cpu_graph_image(results_dir)
    memory_image = generate_memory_graph_image(results_dir)
    network_image = generate_network_graph_image(results_dir)
    mongo_query_image, mongo_memory_image = generate_mongo_graph_image(
        results_dir)

    # Skip doing the logs for such a long run for now.
    # log_message_chunks = get_log_message_in_timed_chunks(
    #     controller_log, deployments)
    log_message_chunks = defaultdict(defaultdict)

    details = dict(
        cpu_graph=cpu_image,
        memory_graph=memory_image,
        network_graph=network_image,
        mongo_graph=mongo_query_image,
        mongo_memory_graph=mongo_memory_image,
        deployments=deployments,
        log_message_chunks=log_message_chunks
    )

    create_html_report(results_dir, details)

def get_log_message_in_timed_chunks(log_file, deployments):
    """Breakdown log into timechunks based on event timeranges in 'deployments'

    """
    deploy_timings = [d.timings for d in deployments['deploys']]

    bootstrap = deployments['bootstrap']
    cleanup = deployments['cleanup']
    all_event_timings = [bootstrap] + deploy_timings + [cleanup]

    raw_details = breakdown_log_by_timeframes(log_file, all_event_timings)

    bs_name = _render_ds_string(bootstrap.start, bootstrap.end)
    cleanup_name = _render_ds_string(cleanup.start, cleanup.end)

    name_lookup = {
        bs_name: 'Bootstrap',
        cleanup_name: 'Kill-Controller',
    }
    for dep in deployments['deploys']:
        name_range = _render_ds_string(
            dep.timings.start, dep.timings.end)
        name_lookup[name_range] = dep.name

    event_details = defaultdict(defaultdict)
    # Outer-layer (i.e. event)
    for event_range in raw_details.keys():
        event_details[event_range]['name'] = name_lookup[event_range]
        event_details[event_range]['logs'] = []

        for log_range in raw_details[event_range].keys():
            timeframe = log_range
            message = '
'.join(raw_details[event_range][log_range])
            event_details[event_range]['logs'].append(
                dict(
                    timeframe=timeframe,
                    message=message))

    return event_details

def create_html_report(results_dir, details):
    # render the html file to the results dir
    with open('./perf_report_template.html', 'rt') as f:
        template = Template(f.read())

    results_output = os.path.join(results_dir, 'report.html')
    with open(results_output, 'wt') as f:
        f.write(template.render(details))

def generate_graph_image(base_dir, results_dir, name, generator):
    metric_files_dir = os.path.join(os.path.abspath(base_dir), results_dir)
    return create_report_graph(metric_files_dir, base_dir, name, generator)

def create_report_graph(rrd_dir, output_dir, name, generator):
    any_file = os.listdir(rrd_dir)[0]
    start, end = get_duration_points(os.path.join(rrd_dir, any_file))
    output_file = os.path.join(
        os.path.abspath(output_dir), '{}.png'.format(name))
    generator(start, end, rrd_dir, output_file)
    print('Created: {}'.format(output_file))
    return output_file

def generate_cpu_graph_image(results_dir):
    return generate_graph_image(
        results_dir, 'aggregation-cpu-average', 'cpu', perf_graphing.cpu_graph)

def generate_memory_graph_image(results_dir):
    return generate_graph_image(
        results_dir, 'memory', 'memory', perf_graphing.memory_graph)

def generate_network_graph_image(results_dir):
    return generate_graph_image(
        results_dir, 'interface-eth0', 'network', perf_graphing.network_graph)

def generate_mongo_graph_image(results_dir):
    dest_path = os.path.join(results_dir, 'mongodb')

    if not perf_graphing.create_mongodb_rrd_file(results_dir, dest_path):
        log.error('Failed to create the MongoDB RRD file.')
        return None
    query_graph = generate_graph_image(
        results_dir, 'mongodb', 'mongodb', perf_graphing.mongodb_graph)

    memory_graph = generate_graph_image(
        results_dir,
        'mongodb',
        'mongodb_memory',
        perf_graphing.mongodb_memory_graph)

    return query_graph, memory_graph

def get_duration_points(rrd_file):
    start = rrdtool.first(rrd_file)
    end = rrdtool.last(rrd_file)

    command = [
        'rrdtool', 'fetch', rrd_file, 'AVERAGE',
        '--start', str(start), '--end', str(end)]
    output = subprocess.check_output(command)

    actual_start = find_actual_start(output)

    return actual_start, end

def find_actual_start(fetch_output):
    # Gets a start timestamp this isn't a NaN.
    for line in fetch_output.splitlines():
        try:
            timestamp, value = line.split(':', 1)
            if not value.startswith(' -nan'):
                return timestamp
        except ValueError:
            pass

class Rest:
    short = MINUTE * 1
    medium = MINUTE * 30
    long = MINUTE * 60
    really_long = MINUTE * 120

def assess_longrun_perf(client, test_length=600):
    longrun_start = datetime.utcnow()
    run_count = 0
    for _ in until_timeout(test_length):
        # Maybe choose one of x different charms/bundles to use.
        # bundle_name = 'cs:bundle/wiki-simple-0'
        # applications = ['mysql', 'wiki']
        applications = ['dummy-sink']
        new_client = action_create(client)
        action_busy(new_client, applications)
        action_cleanup(new_client)

        action_rest(Rest.medium)
        run_count += 1

    longrun_end = datetime.utcnow()
    timing_data = TimingData(longrun_start, longrun_end)
    return DeployDetails(
        'Longrun for {}.'.format(test_length/60/60),
        {'Total action runs': run_count},
        timing_data
    )

def action_create(client):
    new_model = client.add_model(client.env.clone('newmodel'))

    deploy_dummy_stack(new_model, 'trusty')
    new_model.wait_for_workloads()

    return new_model

def action_busy(client, applications):
    client.get_status()

    for app in applications:
        client.juju('add-unit', (app, '-n', '1'))
        client.wait_for_started()
        client.wait_for_workloads()

    for _ in until_timeout(MINUTE*2):
        log.info('Checking status ping.')
        client.show_status()
        log.info('Sleeping . . .')
        time.sleep(MINUTE)

def action_cleanup(client):
    client.destroy_model()
    action_rest(Rest.short)

def action_rest(rest_length=Rest.short):
    log.info('Resting for {} seconds'.format(rest_length))
    time.sleep(rest_length)

def assess_deployment_perf(client, bundle_name='cs:ubuntu'):
    # This is where multiple services are started either at the same time
    # or one after the other etc.
    deploy_start = datetime.utcnow()

    # We possibly want 2 timing details here, one for started (i.e. agents
    # ready) and the other for the workloads to be complete.
    client.deploy(bundle_name)
    client.wait_for_started()
    client.wait_for_workloads()

    deploy_end = datetime.utcnow()
    deploy_timing = TimingData(deploy_start, deploy_end)

    client_details = get_client_details(client)

    return DeployDetails(bundle_name, client_details, deploy_timing)

def get_client_details(client):
    status = client.get_status()
    units = dict()
    for name in status.get_applications().keys():
        units[name] = status.get_service_unit_count(name)
    return units

def setup_system_monitoring(admin_client):
    # Using ssh get into the machine-0 (or all api/state servers)
    # Install the required packages and start up logging of systems collections
    # and mongodb details.

    installer_script_path = _get_static_script_path(SETUP_SCRIPT_PATH)
    collectd_config_path = _get_static_script_path(COLLECTD_CONFIG_PATH)
    installer_script_dest_path = '/tmp/installer.sh'
    runner_script_dest_path = '/tmp/runner.sh'
    collectd_config_dest_file = '/tmp/collectd.config'

    admin_client.juju(
        'scp',
        (collectd_config_path, '0:{}'.format(collectd_config_dest_file)))

    admin_client.juju(
        'scp',
        (installer_script_path, '0:{}'.format(installer_script_dest_path)))
    admin_client.juju('ssh', ('0', 'chmod +x {}'.format(
        installer_script_dest_path)))
    admin_client.juju(
        'ssh',
        ('0', '{installer} {config_file} {output_file}'.format(
            installer=installer_script_dest_path,
            config_file=collectd_config_dest_file,
            output_file=runner_script_dest_path)))

    # Start collection
    # Respawn incase the initial execution fails for whatever reason.
    admin_client.juju('ssh', ('0', '--', 'daemon --respawn {}'.format(
        runner_script_dest_path)))

def _get_static_script_path(script_path):
    full_path = os.path.abspath(__file__)
    current_dir = os.path.dirname(full_path)
    return os.path.join(current_dir, script_path)

def parse_args(argv):
    """Parse all arguments."""
    parser = argparse.ArgumentParser(description="Simple perf/scale testing")
    add_basic_testing_arguments(parser)
    return parser.parse_args(argv)

def main(argv=None):
    args = parse_args(argv)
    configure_logging(args.verbose)
    bs_manager = BootstrapManager.from_args(args)
    assess_perf_test_simple(bs_manager, args.upload_tools)

    return 0

if __name__ == '__main__':
    sys.exit(main())
<|endoftext|>


<commit_before>import requests
import queries
import dmails
import wiki
from requests.auth import HTTPBasicAuth

class api(object):

	def __init__(self, user, key):
		self.user = user
		self.key = key

	def create_image(self, image_id):
		image_table = requests.get(("http://danbooru.donmai.us/posts/" + image_id + ".json"), auth=HTTPBasicAuth(self.user, self.key))
		image = queries.post(image_table, image_id, self)

		return image

	def search_images(self, tag_string, pages):
		results = []
		for i in xrange(pages):
			search = requests.get(("http://danbooru.donmai.us/posts.json?limit=20&page=" + str(i) + "&tags=" + tag_string), auth=HTTPBasicAuth(self.user, self.key))
			table = search.json()
			for posts in table:
				img = queries.post(posts, None, self)
				results.append(img)

		return results

	def upload(self, filepath, rating, tag_string, source=""):
		files = {'file': open(filepath)}
		requests.post(("http://danbooru.donmai.us/uploads.json?upload[file]=" + filepath + "&upload[source]=" + source + "?upload[rating]=" + rating + "?upload[tag_string]=" + tag_string),
			auth=HTTPBasicAuth(self.user, self.key), files=files)

	def get_dmails(self):
		dmails = []
		mailbox = requests.get("http://danbooru.donmai.us/dmails.json", auth=HTTPBasicAuth(self.user, self.key))
		for mail in mailbox:
			msg = dmails.dmail(msg, self)
			dmails.append(msg)

		return dmails

	def send_dmail(self, title, body, to):
		requests.post("http//danbooru.donmai.us/dmails.json?dmail[to_name]=" + to + "&dmail[title]=" + title + "&dmail[body]=" + body, auth=HTTPBasicAuth(self.user, self.key))

	def create_entry(self, title, body):
		requests.post(("http://danbooru.donmai.us/wiki_pages.json?wiki_page[title]=" + str(title) + "&wiki_page[body]=" + str(body))
					 auth=HTTPBasicAuth(self.user, self.key))

	def get_entries(self, terms):
		search = requests.get(("http//danbooru.donmai.us/wiki_pages.jsonn?search[title]=" str(terms)), auth=HTTPBasicAuth(self.user, self.key))
		results = []
		table = search.json()
		for entries in table:
			entry = wiki.entry(entries, self)
			results.append(entry)

<commit_msg>fucking typos will be the death of me<commit_after>import requests
import queries
import dmails
import wiki
from requests.auth import HTTPBasicAuth

class api(object):

	def __init__(self, user, key):
		self.user = user
		self.key = key

	def create_image(self, image_id):
		image_table = requests.get(("http://danbooru.donmai.us/posts/" + image_id + ".json"), auth=HTTPBasicAuth(self.user, self.key))
		image = queries.post(image_table, image_id, self)

		return image

	def search_images(self, tag_string, pages):
		results = []
		for i in xrange(pages):
			search = requests.get(("http://danbooru.donmai.us/posts.json?limit=20&page=" + str(i) + "&tags=" + tag_string), auth=HTTPBasicAuth(self.user, self.key))
			table = search.json()
			for posts in table:
				img = queries.post(posts, None, self)
				results.append(img)

		return results

	def upload(self, filepath, rating, tag_string, source=""):
		files = {'file': open(filepath)}
		requests.post(("http://danbooru.donmai.us/uploads.json?upload[file]=" + filepath + "&upload[source]=" + source + "?upload[rating]=" + rating + "?upload[tag_string]=" + tag_string),
			auth=HTTPBasicAuth(self.user, self.key), files=files)

	def get_dmails(self):
		dmails = []
		mailbox = requests.get("http://danbooru.donmai.us/dmails.json", auth=HTTPBasicAuth(self.user, self.key))
		for mail in mailbox:
			msg = dmails.dmail(msg, self)
			dmails.append(msg)

		return dmails

	def send_dmail(self, title, body, to):
		requests.post("http//danbooru.donmai.us/dmails.json?dmail[to_name]=" + to + "&dmail[title]=" + title + "&dmail[body]=" + body, auth=HTTPBasicAuth(self.user, self.key))

	def create_entry(self, title, body):
		requests.post(("http://danbooru.donmai.us/wiki_pages.json?wiki_page[title]=" + str(title) + "&wiki_page[body]=" + str(body)),
					 auth=HTTPBasicAuth(self.user, self.key))

	def get_entries(self, terms):
		search = requests.get(("http//danbooru.donmai.us/wiki_pages.jsonn?search[title]=" str(terms)), auth=HTTPBasicAuth(self.user, self.key))
		results = []
		table = search.json()
		for entries in table:
			entry = wiki.entry(entries, self)
			results.append(entry)

<|endoftext|>


<commit_before>#!/usr/bin/pythonw
# -*- coding: utf-8 -*-

from appscript import app, k, its, CommandError
from AppKit import NSApplication, NSApplicationDefined, NSBeep, NSSystemDefined, NSURL, NSWorkspace
from Foundation import NSDistributedNotificationCenter
from PyObjCTools import AppHelper
from Carbon.CarbonEvt import RegisterEventHotKey, GetApplicationEventTarget
from Carbon.Events import cmdKey, shiftKey, controlKey
import struct
import scrape
import HotKey

GROWL_APP_NAME = 'StreamVision'
NOTIFICATION_TRACK_INFO = 'iTunes Track Info'
NOTIFICATIONS_ALL = [NOTIFICATION_TRACK_INFO]

kEventHotKeyPressedSubtype = 6
kEventHotKeyReleasedSubtype = 9

kHIDUsage_Csmr_ScanNextTrack = 0xB5
kHIDUsage_Csmr_ScanPreviousTrack = 0xB6
kHIDUsage_Csmr_PlayOrPause = 0xCD

growl = app('GrowlHelperApp')

growl.register(
    as_application=GROWL_APP_NAME,
    all_notifications=NOTIFICATIONS_ALL,
    default_notifications=NOTIFICATIONS_ALL,
    icon_of_application='iTunes.app')
    # if we leave off the .app, we can get Classic iTunes's icon

def growlNotify(title, description, **kw):
    growl.notify(
        with_name=NOTIFICATION_TRACK_INFO,
        title=title,
        description=description,
        application_name=GROWL_APP_NAME,
        **kw)

def radioParadiseURL():
    session = scrape.Session()
    session.go('http://www2.radioparadise.com/nowplay_b.php')
    return session.region.firsttag('a')['href']

def cleanStreamTitle(title):
    if title == k.MissingValue:
        return ''
    title = title.split(' [')[0] # XXX move to description
    title = title.replace('`', u'’')
    return title

def cleanStreamTrackName(name):
    name = name.split('. ')[0]
    name = name.split(': ')[0]
    name = name.split(' - ')
    if len(name) > 1:
        name = ' - '.join(name[:-1])
    else:
        name = name[0]
    return name

def iTunesApp(): return app(id='com.apple.iTunes')
def XTensionApp(): return app(creator='SHEx')

HAVE_XTENSION = False
try:
    XTensionApp()
    HAVE_XTENSION = True
except:
    pass

class StreamVision(NSApplication):

    hotKeyActions = {}
    hotKeys = []

    def displayTrackInfo(self):
        iTunes = iTunesApp()

        trackClass = iTunes.current_track.class_.get()
        trackName = ''
        if trackClass != k.Property:
            trackName = iTunes.current_track.name.get()

        if iTunes.player_state.get() != k.playing:
            growlNotify('iTunes is not playing.', trackName)
            return
        if trackClass == k.URL_track:
            growlNotify(cleanStreamTitle(iTunes.current_stream_title.get()),
                        cleanStreamTrackName(trackName))
            return
        if trackClass == k.Property:
           growlNotify('iTunes is playing.', '')
           return
        kw = {}
        # XXX iTunes doesn't let you get artwork for shared tracks
        if trackClass != k.shared_track:
            artwork = iTunes.current_track.artworks.get()
            if artwork:
                kw['pictImage'] = artwork[0].data.get()
        growlNotify(trackName + '  ' +
                    '★' * (iTunes.current_track.rating.get() / 20),
                    iTunes.current_track.album.get() + "\n" +
                    iTunes.current_track.artist.get(),
                    **kw)

    def goToSite(self):
        iTunes = iTunesApp()
        if iTunes.player_state.get() == k.playing:
            url = iTunes.current_stream_URL.get()
            if url:
                if 'radioparadise.com' in url and 'review' not in url:
                    url = radioParadiseURL()
                NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_(url))
                return
        NSBeep()

    def registerHotKey(self, func, keyCode, mods=0):
        hotKeyRef = RegisterEventHotKey(keyCode, mods, (0, 0),
                                        GetApplicationEventTarget(), 0)
        self.hotKeys.append(hotKeyRef)
        self.hotKeyActions[HotKey.HotKeyAddress(hotKeyRef)] = func
        return hotKeyRef

    def unregisterHotKey(self, hotKeyRef):
        self.hotKeys.remove(hotKeyRef)
        del self.hotKeyActions[HotKey.HotKeyAddress(hotKeyRef)]
        hotKeyRef.UnregisterEventHotKey()

    def incrementRatingBy(self, increment):
        iTunes = iTunesApp()
        rating = iTunes.current_track.rating.get()
        rating += increment
        if rating < 0:
            rating = 0
            NSBeep()
        elif rating > 100:
            rating = 100
            NSBeep()
        iTunes.current_track.rating.set(rating)

    def playPause(self, useStereo=True):
        iTunes = iTunesApp()
        was_playing = (iTunes.player_state.get() == k.playing)
        iTunes.playpause()
        if not was_playing and iTunes.player_state.get() == k.stopped:
            # most likely, we're focused on the iPod, so playing does nothing
            iTunes.browser_windows[1].view.set(iTunes.user_playlists[its.name=='Stations'][1].get())
            iTunes.play()
        if HAVE_XTENSION and useStereo:
            if iTunes.player_state.get() == k.playing:
                XTensionApp().turnon('Stereo')
            else:
                XTensionApp().turnoff('Stereo')

    def playPauseFront(self):
        systemEvents = app(id='com.apple.systemEvents')
        frontName = systemEvents.processes[its.frontmost][1].name()
	if frontName == 'RealPlayer':
	    realPlayer = app(id='com.RealNetworks.RealPlayer')
	    if realPlayer.players[0].state.get() == k.playing:
		realPlayer.pause()
	    else:
		realPlayer.play()
	elif frontName == 'VLC':
	    app(id='org.videolan.vlc').play() # equivalent to playpause
	else:
	    self.playPause(useStereo=False)

    def registerZoomWindowHotKey(self):
        self.zoomWindowHotKey = self.registerHotKey(self.zoomWindow, 42, cmdKey | controlKey) # cmd-ctrl-\

    def unregisterZoomWindowHotKey(self):
        self.unregisterHotKey(self.zoomWindowHotKey)
        self.zoomWindowHotKey = None

    def zoomWindow(self):
        # XXX detect if "enable access for assistive devices" needs to be enabled
        systemEvents = app(id='com.apple.systemEvents')
        frontName = systemEvents.processes[its.frontmost][1].name()
        if frontName == 'iTunes':
            systemEvents.processes['iTunes'].menu_bars[1]. \
                menu_bar_items['Window'].menus.menu_items['Zoom'].click()
            return
        elif frontName in ('X11', 'Emacs'): # preserve C-M-\
            self.unregisterZoomWindowHotKey()
            systemEvents.key_code(42, using=[k.command_down, k.control_down])
            self.registerZoomWindowHotKey()
            return
        try:
            zoomed = app(frontName).windows[1].zoomed
            zoomed.set(not zoomed())
        except (CommandError, RuntimeError):
            systemEvents.processes[frontName].windows \
                [its.subrole == 'AXStandardWindow'].windows[1]. \
                buttons[its.subrole == 'AXZoomButton'].buttons[1].click()

    def finishLaunching(self):
        super(StreamVision, self).finishLaunching()
        self.registerHotKey(self.displayTrackInfo, 100) # F8
        self.registerHotKey(self.goToSite, 100, cmdKey) # cmd-F8
        self.registerHotKey(self.playPause, 101) # F9
        self.registerHotKey(lambda: iTunesApp().previous_track(), 109) # F10
        self.registerHotKey(lambda: iTunesApp().next_track(), 103) # F11
        self.registerHotKey(lambda: self.incrementRatingBy(-20), 109, shiftKey) # shift-F10
        self.registerHotKey(lambda: self.incrementRatingBy(20), 103, shiftKey) # shift-F11
        self.registerZoomWindowHotKey()
        NSDistributedNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, self.displayTrackInfo, 'com.apple.iTunes.playerInfo', None)
        try:
            import HIDRemote
            HIDRemote.connect()
        except ImportError:
            print "failed to import HIDRemote (XXX fix - on Intel)"
        except OSError, e:
            print "failed to connect to remote: ", e

    def sendEvent_(self, theEvent):
        eventType = theEvent.type()
        if eventType == NSSystemDefined and \
               theEvent.subtype() == kEventHotKeyPressedSubtype:
            self.hotKeyActions[theEvent.data1()]()
        elif eventType == NSApplicationDefined:
            key = theEvent.data1()
            if key == kHIDUsage_Csmr_ScanNextTrack:
                iTunesApp().next_track()
            elif key == kHIDUsage_Csmr_ScanPreviousTrack:
                iTunesApp().previous_track()
            elif key == kHIDUsage_Csmr_PlayOrPause:
                self.playPauseFront()
        super(StreamVision, self).sendEvent_(theEvent)

if __name__ == "__main__":
    AppHelper.runEventLoop()
    HIDRemote.disconnect() # XXX do we get here?
<commit_msg>StreamVision.py: Added more support for recent appscript versions (k.Property => k.property).  Removed unnecessary .get invocations since we've dropped support for older appscript versions where it was required.  Robustified RealPlayer support.<commit_after>#!/usr/bin/pythonw
# -*- coding: utf-8 -*-

from appscript import app, k, its, CommandError
from AppKit import NSApplication, NSApplicationDefined, NSBeep, NSSystemDefined, NSURL, NSWorkspace
from Foundation import NSDistributedNotificationCenter
from PyObjCTools import AppHelper
from Carbon.CarbonEvt import RegisterEventHotKey, GetApplicationEventTarget
from Carbon.Events import cmdKey, shiftKey, controlKey
import struct
import scrape
import HotKey

GROWL_APP_NAME = 'StreamVision'
NOTIFICATION_TRACK_INFO = 'iTunes Track Info'
NOTIFICATIONS_ALL = [NOTIFICATION_TRACK_INFO]

kEventHotKeyPressedSubtype = 6
kEventHotKeyReleasedSubtype = 9

kHIDUsage_Csmr_ScanNextTrack = 0xB5
kHIDUsage_Csmr_ScanPreviousTrack = 0xB6
kHIDUsage_Csmr_PlayOrPause = 0xCD

growl = app('GrowlHelperApp')

growl.register(
    as_application=GROWL_APP_NAME,
    all_notifications=NOTIFICATIONS_ALL,
    default_notifications=NOTIFICATIONS_ALL,
    icon_of_application='iTunes.app')
    # if we leave off the .app, we can get Classic iTunes's icon

def growlNotify(title, description, **kw):
    growl.notify(
        with_name=NOTIFICATION_TRACK_INFO,
        title=title,
        description=description,
        application_name=GROWL_APP_NAME,
        **kw)

def radioParadiseURL():
    # XXX better to use http://www2.radioparadise.com/playlist.xml ?
    session = scrape.Session()
    session.go('http://www2.radioparadise.com/nowplay_b.php')
    return session.region.firsttag('a')['href']

def cleanStreamTitle(title):
    if title == k.MissingValue:
        return ''
    title = title.split(' [')[0] # XXX move to description
    title = title.replace('`', u'’')
    return title

def cleanStreamTrackName(name):
    name = name.split('. ')[0]
    name = name.split(': ')[0]
    name = name.split(' - ')
    if len(name) > 1:
        name = ' - '.join(name[:-1])
    else:
        name = name[0]
    return name

def iTunesApp(): return app(id='com.apple.iTunes')
def XTensionApp(): return app(creator='SHEx')

HAVE_XTENSION = False
try:
    XTensionApp()
    HAVE_XTENSION = True
except:
    pass

class StreamVision(NSApplication):

    hotKeyActions = {}
    hotKeys = []

    def displayTrackInfo(self):
        iTunes = iTunesApp()

        trackClass = iTunes.current_track.class_()
        trackName = ''
        if trackClass != k.property:
            trackName = iTunes.current_track.name()

        if iTunes.player_state() != k.playing:
            growlNotify('iTunes is not playing.', trackName)
            return
        if trackClass == k.URL_track:
            growlNotify(cleanStreamTitle(iTunes.current_stream_title()),
                        cleanStreamTrackName(trackName))
            return
        if trackClass == k.property:
           growlNotify('iTunes is playing.', '')
           return
        kw = {}
        # XXX iTunes doesn't let you get artwork for shared tracks
        if trackClass != k.shared_track:
            artwork = iTunes.current_track.artworks()
            if artwork:
                kw['pictImage'] = artwork[0].data()
        growlNotify(trackName + '  ' +
                    '★' * (iTunes.current_track.rating() / 20),
                    iTunes.current_track.album() + "\n" +
                    iTunes.current_track.artist(),
                    **kw)

    def goToSite(self):
        iTunes = iTunesApp()
        if iTunes.player_state() == k.playing:
            url = iTunes.current_stream_URL()
            if url:
                if 'radioparadise.com' in url and 'review' not in url:
                    url = radioParadiseURL()
                NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_(url))
                return
        NSBeep()

    def registerHotKey(self, func, keyCode, mods=0):
        hotKeyRef = RegisterEventHotKey(keyCode, mods, (0, 0),
                                        GetApplicationEventTarget(), 0)
        self.hotKeys.append(hotKeyRef)
        self.hotKeyActions[HotKey.HotKeyAddress(hotKeyRef)] = func
        return hotKeyRef

    def unregisterHotKey(self, hotKeyRef):
        self.hotKeys.remove(hotKeyRef)
        del self.hotKeyActions[HotKey.HotKeyAddress(hotKeyRef)]
        hotKeyRef.UnregisterEventHotKey()

    def incrementRatingBy(self, increment):
        iTunes = iTunesApp()
        rating = iTunes.current_track.rating()
        rating += increment
        if rating < 0:
            rating = 0
            NSBeep()
        elif rating > 100:
            rating = 100
            NSBeep()
        iTunes.current_track.rating.set(rating)

    def playPause(self, useStereo=True):
        iTunes = iTunesApp()
        was_playing = (iTunes.player_state() == k.playing)
        iTunes.playpause()
        if not was_playing and iTunes.player_state() == k.stopped:
            # most likely, we're focused on the iPod, so playing does nothing
            iTunes.browser_windows[1].view.set(iTunes.user_playlists[its.name=='Stations'][1]())
            iTunes.play()
        if HAVE_XTENSION and useStereo:
            if iTunes.player_state() == k.playing:
                XTensionApp().turnon('Stereo')
            else:
                XTensionApp().turnoff('Stereo')

    def playPauseFront(self):
        systemEvents = app(id='com.apple.systemEvents')
        frontName = systemEvents.processes[its.frontmost][1].name()
	if frontName == 'RealPlayer':
	    realPlayer = app(id='com.RealNetworks.RealPlayer')
	    if len(realPlayer.players()) > 0:
		if realPlayer.players[1].state() == k.playing:
		    realPlayer.pause()
		else:
		    realPlayer.play()
		return
	elif frontName == 'VLC':
	    app(id='org.videolan.vlc').play() # equivalent to playpause
	else:
	    self.playPause(useStereo=False)

    def registerZoomWindowHotKey(self):
        self.zoomWindowHotKey = self.registerHotKey(self.zoomWindow, 42, cmdKey | controlKey) # cmd-ctrl-\

    def unregisterZoomWindowHotKey(self):
        self.unregisterHotKey(self.zoomWindowHotKey)
        self.zoomWindowHotKey = None

    def zoomWindow(self):
        # XXX detect if "enable access for assistive devices" needs to be enabled
        systemEvents = app(id='com.apple.systemEvents')
        frontName = systemEvents.processes[its.frontmost][1].name()
        if frontName == 'iTunes':
            systemEvents.processes['iTunes'].menu_bars[1]. \
                menu_bar_items['Window'].menus.menu_items['Zoom'].click()
            return
        elif frontName in ('X11', 'Emacs'): # preserve C-M-\
            self.unregisterZoomWindowHotKey()
            systemEvents.key_code(42, using=[k.command_down, k.control_down])
            self.registerZoomWindowHotKey()
            return
        try:
            zoomed = app(frontName).windows[1].zoomed
            zoomed.set(not zoomed())
        except (CommandError, RuntimeError):
            systemEvents.processes[frontName].windows \
                [its.subrole == 'AXStandardWindow'].windows[1]. \
                buttons[its.subrole == 'AXZoomButton'].buttons[1].click()

    def finishLaunching(self):
        super(StreamVision, self).finishLaunching()
        self.registerHotKey(self.displayTrackInfo, 100) # F8
        self.registerHotKey(self.goToSite, 100, cmdKey) # cmd-F8
        self.registerHotKey(self.playPause, 101) # F9
        self.registerHotKey(lambda: iTunesApp().previous_track(), 109) # F10
        self.registerHotKey(lambda: iTunesApp().next_track(), 103) # F11
        self.registerHotKey(lambda: self.incrementRatingBy(-20), 109, shiftKey) # shift-F10
        self.registerHotKey(lambda: self.incrementRatingBy(20), 103, shiftKey) # shift-F11
        self.registerZoomWindowHotKey()
        NSDistributedNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, self.displayTrackInfo, 'com.apple.iTunes.playerInfo', None)
        try:
            import HIDRemote
            HIDRemote.connect()
        except ImportError:
            print "failed to import HIDRemote (XXX fix - on Intel)"
        except OSError, e:
            print "failed to connect to remote: ", e

    def sendEvent_(self, theEvent):
        eventType = theEvent.type()
        if eventType == NSSystemDefined and \
               theEvent.subtype() == kEventHotKeyPressedSubtype:
            self.hotKeyActions[theEvent.data1()]()
        elif eventType == NSApplicationDefined:
            key = theEvent.data1()
            if key == kHIDUsage_Csmr_ScanNextTrack:
                iTunesApp().next_track()
            elif key == kHIDUsage_Csmr_ScanPreviousTrack:
                iTunesApp().previous_track()
            elif key == kHIDUsage_Csmr_PlayOrPause:
                self.playPauseFront()
        super(StreamVision, self).sendEvent_(theEvent)

if __name__ == "__main__":
    AppHelper.runEventLoop()
    HIDRemote.disconnect() # XXX do we get here?
<|endoftext|>


<commit_before>#!/usr/bin/pythonw
# -*- coding: utf-8 -*-

from aem.ae import newdesc
from appscript import app, k, its, CommandError
from AppKit import (NSApplication, NSApplicationDefined, NSBeep, NSImage,
                    NSSystemDefined, NSURL, NSWorkspace)
from Foundation import (NSDistributedNotificationCenter,
                        NSSearchPathForDirectoriesInDomains,
                        NSCachesDirectory, NSUserDomainMask)
from PyObjCTools import AppHelper
from Carbon.CarbonEvt import RegisterEventHotKey, GetApplicationEventTarget
from Carbon.Events import cmdKey, shiftKey
from AudioDevice import (default_output_device_is_airplay,
                         set_default_output_device_changed_callback)
import httplib2
import os
import scrape
import HotKey

GROWL_APP_NAME = 'StreamVision'
NOTIFICATION_TRACK_INFO = 'iTunes Track Info'
NOTIFICATIONS_ALL = [NOTIFICATION_TRACK_INFO]

kEventHotKeyPressedSubtype = 6
kEventHotKeyReleasedSubtype = 9

def growlRegister():
    global growl
    growl = app(id='com.Growl.GrowlHelperApp')

    growl.register(
        as_application=GROWL_APP_NAME,
        all_notifications=NOTIFICATIONS_ALL,
        default_notifications=NOTIFICATIONS_ALL,
        icon_of_application='iTunes.app')
        # if we leave off the .app, we can get Classic iTunes's icon

def growlNotify(title, description, **kw):
    try:
        if usingStereo:
            description += '\n(AirPlay)'

        if 'image' in kw:
            image = (NSImage.alloc().initWithData_(buffer(kw['image']))
                     .TIFFRepresentation())
            kw['image'] = newdesc('TIFF', image)

        growl.notify(
            with_name=NOTIFICATION_TRACK_INFO,
            title=title,
            description=description,
            application_name=GROWL_APP_NAME,
            **kw)
    except CommandError:
        growlRegister()
        growlNotify(title, description, **kw)

def radioParadiseURL():
    session = scrape.Session()
    session.go('http://radioparadise.com/jq_playlist.php')
    url = session.region.firsttag('a')['href']
    if not url.startswith('http'):
        url = 'http://www.radioparadise.com/rp2-' + url
        return url

def cleanStreamTitle(title):
    if title == k.missing_value:
        return ''
    title = title.split(' [')[0] # XXX move to description
    try: # incorrectly encoded?
        title = title.encode('iso-8859-1').decode('utf-8')
    except (UnicodeDecodeError, UnicodeEncodeError):
        pass
    title = title.replace('`', u'’')
    return title

def cleanStreamTrackName(name):
    name = name.split('. ')[0]
    name = name.split(': ')[0]
    name = name.split(' - ')
    if len(name) > 1:
        name = ' - '.join(name[:-1])
    else:
        name = name[0]
    return name

def HermesApp(): return app(id='com.alexcrichton.Hermes')
def iTunesApp(): return app(id='com.apple.iTunes')
def XTensionApp(): return app(creator='SHEx')

HAVE_XTENSION = False
try:
    XTensionApp()
    HAVE_XTENSION = True
except:
    pass

needsStereoPowerOn = HAVE_XTENSION
usingStereo = False

def mayUseStereo():
    if not HAVE_XTENSION:
        return False
    try:
        # A bit better in iTunes 11.0.3, but can't do this via an Apple
        # Event descriptor; have to send multiple events
        return any(d.kind() != k.computer
                   for d in iTunesApp().current_AirPlay_devices())
    except AttributeError:
        pass

    systemEvents = app(id='com.apple.systemEvents')
    iTunesWindow = systemEvents.application_processes[u'iTunes'].windows[u'iTunes']
    # Can't get AirPlay status with iTunes Mini Player or window on other Space.
    try:
        remote_speakers = iTunesWindow.buttons[its.attributes['AXDescription'].value.beginswith(u'AirPlay')].title()
    except CommandError: # window on another Space?
        return usingStereo
    return remote_speakers and remote_speakers[0] not in (None, k.missing_value)

def turnStereoOnOrOff():
    global needsStereoPowerOn, usingStereo
    usingStereo = False
    if not default_output_device_is_airplay() and not mayUseStereo():
        if HAVE_XTENSION and XTensionApp().status('Stereo'):
            XTensionApp().turnoff('Stereo')
        return
    if not XTensionApp().status('Stereo'):
        XTensionApp().turnon('Stereo')
    usingStereo = True
    needsStereoPowerOn = False

def turnStereoOff():
    global needsStereoPowerOn, usingStereo
    usingStereo = False
    if default_output_device_is_airplay() or not mayUseStereo():
        return
    if not needsStereoPowerOn and XTensionApp().status('Stereo'):
        XTensionApp().turnoff('Stereo')
    needsStereoPowerOn = True

def notifyTrackInfo(name, album=None, artist=None, rating=0, hasArtwork=False,
                    streamTitle=None, streamURL=None, playing=True, onChange=False):
    if not playing:
        growlNotify('iTunes is not playing.', name)
        return
    turnStereoOnOrOff()

    if streamURL:
        kw = {}
        if streamURL and streamURL.endswith('.jpg'):
            try:
                response, content = http.request(streamURL)
            except Exception, e:
                import sys
                print >> sys.stderr, 'Request for album art failed:', e
            else:
                if response['content-type'].startswith('image/'):
                    kw['image'] = content
        growlNotify(cleanStreamTitle(streamTitle),
                    cleanStreamTrackName(name), **kw)
        return

    if not name:
        growlNotify('iTunes is playing.', '')
        return

    kw = {}
    if hasArtwork:
        try:
            kw['image'] = iTunesApp().current_track.artworks[1].data_().data
        except CommandError:
            pass

    growlNotify(name + '  ' + u'★' * (rating / 20),
                (album or '') + '\n' + (artist or ''),
                **kw)

class OneFileCache(object):
    __slots__ = ('key', 'cache')

    def __init__(self, cache):
        if not os.path.exists(cache):
            os.makedirs(cache)
        self.cache = os.path.join(cache, 'file')
        self.key = None

    def get(self, key):
        if key == self.key:
            return file(self.cache, 'r').read()

    def set(self, key, value):
        self.key = key
        file(self.cache, 'w').write(value)

    def delete(self, key):
        if key == self.key:
            self.key = None
            os.remove(self.cache)

class StreamVision(NSApplication):

    hotKeyActions = {}
    hotKeys = []

    def playerInfoChanged(self, playerInfo):
        infoDict = dict(playerInfo.userInfo())
        trackName = infoDict.get('Name', '')
        playerState = infoDict.get('Player State')
        if playerState != 'Playing':
            notifyTrackInfo(trackName, playing=False, onChange=True)
            return
        url = infoDict.get('Stream URL')
        if url:
            notifyTrackInfo(trackName, streamTitle=infoDict.get('Stream Title'),
                            streamURL=url, onChange=True)
            return
        artworkCount = int(infoDict.get('Artwork Count', 0))
        # XXX When starting iTunes Radio station playback, we get 2 notifications,
        # neither of which mention the track: the first has the station's artwork,
        # the second doesn't.  On the 2nd notification, wait 10 seconds and try again.
        # By then, we should have artwork.
        if not infoDict.has_key('Total Time') and artworkCount == 0:
            self.performSelector_withObject_afterDelay_(self.displayTrackInfo, None, 10)
            return
        notifyTrackInfo(trackName, infoDict.get('Album'), infoDict.get('Artist'),
                        infoDict.get('Rating', 0), artworkCount > 0, onChange=True)

    def displayTrackInfo(self):
        iTunes = iTunesApp()

        try:
            trackClass = iTunes.current_track.class_()
        except CommandError:
            trackClass = k.property

        trackName = ''
        if trackClass != k.property:
            trackName = iTunes.current_track.name()

        try:
            playerState = iTunes.player_state()
        except CommandError:
            playerState = None # probably iTunes quit
        if playerState != k.playing:
            notifyTrackInfo(trackName, playing=False)
            return
        if trackClass == k.URL_track:
            url = iTunes.current_stream_URL()
            if url == k.missing_value:
                url = None
            notifyTrackInfo(trackName, streamTitle=iTunes.current_stream_title(),
                            streamURL=url)
            return
        if trackClass == k.property:
            notifyTrackInfo(None)
            return
        # XXX iTunes doesn't let you get artwork for shared tracks (still?)
        notifyTrackInfo(trackName, iTunes.current_track.album(), iTunes.current_track.artist(),
                        iTunes.current_track.rating(), trackClass != k.shared_track)

    def defaultOutputDeviceChanged(self):
        turnStereoOnOrOff()
        self.displayTrackInfo()

    def goToSite(self):
        iTunes = iTunesApp()
        if iTunes.player_state() == k.playing:
            url = iTunes.current_stream_URL()
            if url != k.missing_value:
                if 'radioparadise.com' in url and 'review' not in url:
                    url = radioParadiseURL()
                NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_(url))
                return
        NSBeep()

    def registerHotKey(self, func, keyCode, mods=0):
        hotKeyRef = RegisterEventHotKey(keyCode, mods, (0, 0),
                                        GetApplicationEventTarget(), 0)
        self.hotKeys.append(hotKeyRef)
        self.hotKeyActions[HotKey.HotKeyAddress(hotKeyRef)] = func
        return hotKeyRef

    def unregisterHotKey(self, hotKeyRef):
        self.hotKeys.remove(hotKeyRef)
        del self.hotKeyActions[HotKey.HotKeyAddress(hotKeyRef)]
        hotKeyRef.UnregisterEventHotKey()

    def incrementRatingBy(self, increment):
        iTunes = iTunesApp()
        rating = iTunes.current_track.rating()
        rating += increment
        if rating < 0:
            rating = 0
            NSBeep()
        elif rating > 100:
            rating = 100
            NSBeep()
        iTunes.current_track.rating.set(rating)

    def playPause(self, useStereo=True):
        global needsStereoPowerOn

        Hermes = HermesApp()
        if Hermes.isrunning():
            Hermes.playpause()
            return

        iTunes = iTunesApp()
        was_playing = (iTunes.player_state() == k.playing)
        if not useStereo:
            needsStereoPowerOn = False
        iTunes.playpause()
        if not was_playing and iTunes.player_state() == k.stopped:
            # most likely, we're focused on the iPod, so playing does nothing
            iTunes.browser_windows[1].view.set(iTunes.user_playlists[its.name=='Stations'][1]())
            iTunes.play()
        if not useStereo:
            return
        if iTunes.player_state() == k.playing:
            turnStereoOnOrOff()
        else:
            turnStereoOff()

    def nextTrack(self):
        iTunesApp().next_track()

    def finishLaunching(self):
        global http

        super(StreamVision, self).finishLaunching()

        caches = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
                                                     NSUserDomainMask, True)[0]
        cache = os.path.join(caches, 'StreamVision')
        http = httplib2.Http(OneFileCache(cache), 5)
        self.imagePath = os.path.join(cache, 'image')

        self.registerHotKey(self.displayTrackInfo, 100) # F8
        self.registerHotKey(self.goToSite, 100, cmdKey) # cmd-F8
        self.registerHotKey(self.playPause, 101) # F9
        self.registerHotKey(lambda: iTunesApp().previous_track(), 109) # F10
        self.registerHotKey(self.nextTrack, 103) # F11
        self.registerHotKey(lambda: self.incrementRatingBy(-20), 109, shiftKey) # shift-F10
        self.registerHotKey(lambda: self.incrementRatingBy(20), 103, shiftKey) # shift-F11

        distributedNotificationCenter = NSDistributedNotificationCenter.defaultCenter()
        distributedNotificationCenter.addObserver_selector_name_object_(self, self.playerInfoChanged, 'com.apple.iTunes.playerInfo', None)
        distributedNotificationCenter.addObserver_selector_name_object_(self, self.terminate_, 'com.apple.logoutContinued', None)

        set_default_output_device_changed_callback(
            self.defaultOutputDeviceChanged)
        turnStereoOnOrOff()

    def sendEvent_(self, theEvent):
        eventType = theEvent.type()
        if eventType == NSSystemDefined and \
               theEvent.subtype() == kEventHotKeyPressedSubtype:
            self.hotKeyActions[theEvent.data1()]()
        super(StreamVision, self).sendEvent_(theEvent)

if __name__ == "__main__":
    growlRegister()
    AppHelper.runEventLoop(installInterrupt=True)
<commit_msg>Support for skipping track in Hermes.<commit_after>#!/usr/bin/pythonw
# -*- coding: utf-8 -*-

from aem.ae import newdesc
from appscript import app, k, its, CommandError
from AppKit import (NSApplication, NSApplicationDefined, NSBeep, NSImage,
                    NSSystemDefined, NSURL, NSWorkspace)
from Foundation import (NSDistributedNotificationCenter,
                        NSSearchPathForDirectoriesInDomains,
                        NSCachesDirectory, NSUserDomainMask)
from PyObjCTools import AppHelper
from Carbon.CarbonEvt import RegisterEventHotKey, GetApplicationEventTarget
from Carbon.Events import cmdKey, shiftKey
from AudioDevice import (default_output_device_is_airplay,
                         set_default_output_device_changed_callback)
import httplib2
import os
import scrape
import HotKey

GROWL_APP_NAME = 'StreamVision'
NOTIFICATION_TRACK_INFO = 'iTunes Track Info'
NOTIFICATIONS_ALL = [NOTIFICATION_TRACK_INFO]

kEventHotKeyPressedSubtype = 6
kEventHotKeyReleasedSubtype = 9

def growlRegister():
    global growl
    growl = app(id='com.Growl.GrowlHelperApp')

    growl.register(
        as_application=GROWL_APP_NAME,
        all_notifications=NOTIFICATIONS_ALL,
        default_notifications=NOTIFICATIONS_ALL,
        icon_of_application='iTunes.app')
        # if we leave off the .app, we can get Classic iTunes's icon

def growlNotify(title, description, **kw):
    try:
        if usingStereo:
            description += '\n(AirPlay)'

        if 'image' in kw:
            image = (NSImage.alloc().initWithData_(buffer(kw['image']))
                     .TIFFRepresentation())
            kw['image'] = newdesc('TIFF', image)

        growl.notify(
            with_name=NOTIFICATION_TRACK_INFO,
            title=title,
            description=description,
            application_name=GROWL_APP_NAME,
            **kw)
    except CommandError:
        growlRegister()
        growlNotify(title, description, **kw)

def radioParadiseURL():
    session = scrape.Session()
    session.go('http://radioparadise.com/jq_playlist.php')
    url = session.region.firsttag('a')['href']
    if not url.startswith('http'):
        url = 'http://www.radioparadise.com/rp2-' + url
        return url

def cleanStreamTitle(title):
    if title == k.missing_value:
        return ''
    title = title.split(' [')[0] # XXX move to description
    try: # incorrectly encoded?
        title = title.encode('iso-8859-1').decode('utf-8')
    except (UnicodeDecodeError, UnicodeEncodeError):
        pass
    title = title.replace('`', u'’')
    return title

def cleanStreamTrackName(name):
    name = name.split('. ')[0]
    name = name.split(': ')[0]
    name = name.split(' - ')
    if len(name) > 1:
        name = ' - '.join(name[:-1])
    else:
        name = name[0]
    return name

def HermesApp(): return app(id='com.alexcrichton.Hermes')
def iTunesApp(): return app(id='com.apple.iTunes')
def XTensionApp(): return app(creator='SHEx')

HAVE_XTENSION = False
try:
    XTensionApp()
    HAVE_XTENSION = True
except:
    pass

needsStereoPowerOn = HAVE_XTENSION
usingStereo = False

def mayUseStereo():
    if not HAVE_XTENSION:
        return False
    try:
        # A bit better in iTunes 11.0.3, but can't do this via an Apple
        # Event descriptor; have to send multiple events
        return any(d.kind() != k.computer
                   for d in iTunesApp().current_AirPlay_devices())
    except AttributeError:
        pass

    systemEvents = app(id='com.apple.systemEvents')
    iTunesWindow = systemEvents.application_processes[u'iTunes'].windows[u'iTunes']
    # Can't get AirPlay status with iTunes Mini Player or window on other Space.
    try:
        remote_speakers = iTunesWindow.buttons[its.attributes['AXDescription'].value.beginswith(u'AirPlay')].title()
    except CommandError: # window on another Space?
        return usingStereo
    return remote_speakers and remote_speakers[0] not in (None, k.missing_value)

def turnStereoOnOrOff():
    global needsStereoPowerOn, usingStereo
    usingStereo = False
    if not default_output_device_is_airplay() and not mayUseStereo():
        if HAVE_XTENSION and XTensionApp().status('Stereo'):
            XTensionApp().turnoff('Stereo')
        return
    if not XTensionApp().status('Stereo'):
        XTensionApp().turnon('Stereo')
    usingStereo = True
    needsStereoPowerOn = False

def turnStereoOff():
    global needsStereoPowerOn, usingStereo
    usingStereo = False
    if default_output_device_is_airplay() or not mayUseStereo():
        return
    if not needsStereoPowerOn and XTensionApp().status('Stereo'):
        XTensionApp().turnoff('Stereo')
    needsStereoPowerOn = True

def notifyTrackInfo(name, album=None, artist=None, rating=0, hasArtwork=False,
                    streamTitle=None, streamURL=None, playing=True, onChange=False):
    if not playing:
        growlNotify('iTunes is not playing.', name)
        return
    turnStereoOnOrOff()

    if streamURL:
        kw = {}
        if streamURL and streamURL.endswith('.jpg'):
            try:
                response, content = http.request(streamURL)
            except Exception, e:
                import sys
                print >> sys.stderr, 'Request for album art failed:', e
            else:
                if response['content-type'].startswith('image/'):
                    kw['image'] = content
        growlNotify(cleanStreamTitle(streamTitle),
                    cleanStreamTrackName(name), **kw)
        return

    if not name:
        growlNotify('iTunes is playing.', '')
        return

    kw = {}
    if hasArtwork:
        try:
            kw['image'] = iTunesApp().current_track.artworks[1].data_().data
        except CommandError:
            pass

    growlNotify(name + '  ' + u'★' * (rating / 20),
                (album or '') + '\n' + (artist or ''),
                **kw)

class OneFileCache(object):
    __slots__ = ('key', 'cache')

    def __init__(self, cache):
        if not os.path.exists(cache):
            os.makedirs(cache)
        self.cache = os.path.join(cache, 'file')
        self.key = None

    def get(self, key):
        if key == self.key:
            return file(self.cache, 'r').read()

    def set(self, key, value):
        self.key = key
        file(self.cache, 'w').write(value)

    def delete(self, key):
        if key == self.key:
            self.key = None
            os.remove(self.cache)

class StreamVision(NSApplication):

    hotKeyActions = {}
    hotKeys = []

    def playerInfoChanged(self, playerInfo):
        infoDict = dict(playerInfo.userInfo())
        trackName = infoDict.get('Name', '')
        playerState = infoDict.get('Player State')
        if playerState != 'Playing':
            notifyTrackInfo(trackName, playing=False, onChange=True)
            return
        url = infoDict.get('Stream URL')
        if url:
            notifyTrackInfo(trackName, streamTitle=infoDict.get('Stream Title'),
                            streamURL=url, onChange=True)
            return
        artworkCount = int(infoDict.get('Artwork Count', 0))
        # XXX When starting iTunes Radio station playback, we get 2 notifications,
        # neither of which mention the track: the first has the station's artwork,
        # the second doesn't.  On the 2nd notification, wait 10 seconds and try again.
        # By then, we should have artwork.
        if not infoDict.has_key('Total Time') and artworkCount == 0:
            self.performSelector_withObject_afterDelay_(self.displayTrackInfo, None, 10)
            return
        notifyTrackInfo(trackName, infoDict.get('Album'), infoDict.get('Artist'),
                        infoDict.get('Rating', 0), artworkCount > 0, onChange=True)

    def displayTrackInfo(self):
        iTunes = iTunesApp()

        try:
            trackClass = iTunes.current_track.class_()
        except CommandError:
            trackClass = k.property

        trackName = ''
        if trackClass != k.property:
            trackName = iTunes.current_track.name()

        try:
            playerState = iTunes.player_state()
        except CommandError:
            playerState = None # probably iTunes quit
        if playerState != k.playing:
            notifyTrackInfo(trackName, playing=False)
            return
        if trackClass == k.URL_track:
            url = iTunes.current_stream_URL()
            if url == k.missing_value:
                url = None
            notifyTrackInfo(trackName, streamTitle=iTunes.current_stream_title(),
                            streamURL=url)
            return
        if trackClass == k.property:
            notifyTrackInfo(None)
            return
        # XXX iTunes doesn't let you get artwork for shared tracks (still?)
        notifyTrackInfo(trackName, iTunes.current_track.album(), iTunes.current_track.artist(),
                        iTunes.current_track.rating(), trackClass != k.shared_track)

    def defaultOutputDeviceChanged(self):
        turnStereoOnOrOff()
        self.displayTrackInfo()

    def goToSite(self):
        iTunes = iTunesApp()
        if iTunes.player_state() == k.playing:
            url = iTunes.current_stream_URL()
            if url != k.missing_value:
                if 'radioparadise.com' in url and 'review' not in url:
                    url = radioParadiseURL()
                NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_(url))
                return
        NSBeep()

    def registerHotKey(self, func, keyCode, mods=0):
        hotKeyRef = RegisterEventHotKey(keyCode, mods, (0, 0),
                                        GetApplicationEventTarget(), 0)
        self.hotKeys.append(hotKeyRef)
        self.hotKeyActions[HotKey.HotKeyAddress(hotKeyRef)] = func
        return hotKeyRef

    def unregisterHotKey(self, hotKeyRef):
        self.hotKeys.remove(hotKeyRef)
        del self.hotKeyActions[HotKey.HotKeyAddress(hotKeyRef)]
        hotKeyRef.UnregisterEventHotKey()

    def incrementRatingBy(self, increment):
        iTunes = iTunesApp()
        rating = iTunes.current_track.rating()
        rating += increment
        if rating < 0:
            rating = 0
            NSBeep()
        elif rating > 100:
            rating = 100
            NSBeep()
        iTunes.current_track.rating.set(rating)

    def playPause(self, useStereo=True):
        global needsStereoPowerOn

        Hermes = HermesApp()
        if Hermes.isrunning():
            Hermes.playpause()
            return

        iTunes = iTunesApp()
        was_playing = (iTunes.player_state() == k.playing)
        if not useStereo:
            needsStereoPowerOn = False
        iTunes.playpause()
        if not was_playing and iTunes.player_state() == k.stopped:
            # most likely, we're focused on the iPod, so playing does nothing
            iTunes.browser_windows[1].view.set(iTunes.user_playlists[its.name=='Stations'][1]())
            iTunes.play()
        if not useStereo:
            return
        if iTunes.player_state() == k.playing:
            turnStereoOnOrOff()
        else:
            turnStereoOff()

    def nextTrack(self):
        Hermes = HermesApp()
        if Hermes.isrunning():
            Hermes.next_song()
            return

        iTunesApp().next_track()

    def finishLaunching(self):
        global http

        super(StreamVision, self).finishLaunching()

        caches = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
                                                     NSUserDomainMask, True)[0]
        cache = os.path.join(caches, 'StreamVision')
        http = httplib2.Http(OneFileCache(cache), 5)
        self.imagePath = os.path.join(cache, 'image')

        self.registerHotKey(self.displayTrackInfo, 100) # F8
        self.registerHotKey(self.goToSite, 100, cmdKey) # cmd-F8
        self.registerHotKey(self.playPause, 101) # F9
        self.registerHotKey(lambda: iTunesApp().previous_track(), 109) # F10
        self.registerHotKey(self.nextTrack, 103) # F11
        self.registerHotKey(lambda: self.incrementRatingBy(-20), 109, shiftKey) # shift-F10
        self.registerHotKey(lambda: self.incrementRatingBy(20), 103, shiftKey) # shift-F11

        distributedNotificationCenter = NSDistributedNotificationCenter.defaultCenter()
        distributedNotificationCenter.addObserver_selector_name_object_(self, self.playerInfoChanged, 'com.apple.iTunes.playerInfo', None)
        distributedNotificationCenter.addObserver_selector_name_object_(self, self.terminate_, 'com.apple.logoutContinued', None)

        set_default_output_device_changed_callback(
            self.defaultOutputDeviceChanged)
        turnStereoOnOrOff()

    def sendEvent_(self, theEvent):
        eventType = theEvent.type()
        if eventType == NSSystemDefined and \
               theEvent.subtype() == kEventHotKeyPressedSubtype:
            self.hotKeyActions[theEvent.data1()]()
        super(StreamVision, self).sendEvent_(theEvent)

if __name__ == "__main__":
    growlRegister()
    AppHelper.runEventLoop(installInterrupt=True)
<|endoftext|>


<commit_before>"""
The serial/USB printer connection. Uses a 2nd python process to connect to the printer so we never
have locking problems where other threads in python can block the USB printing.
"""
__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"

import threading
import time
import platform
import os
import sys
import subprocess
import json

from Cura.util import profile
from Cura.util import machineCom
from Cura.util.printerConnection import printerConnectionBase

class serialConnectionGroup(printerConnectionBase.printerConnectionGroup):
	"""
	The serial connection group. Keeps track of all available serial ports,
	and builds a serialConnection for each port.
	"""
	def __init__(self):
		super(serialConnectionGroup, self).__init__("USB")
		self._connectionMap = {}

	def getAvailableConnections(self):
		if profile.getMachineSetting('serial_port') == 'AUTO':
			serialList = machineCom.serialList(True)
		else:
			serialList = [profile.getMachineSetting('serial_port')]
		for port in serialList:
			if port not in self._connectionMap:
				self._connectionMap[port] = serialConnection(port)
		for key in self._connectionMap.keys():
			if key not in serialList and not self._connectionMap[key].isActiveConnectionOpen():
				self._connectionMap.pop(key)
		return self._connectionMap.values()

	def getIconID(self):
		return 6

	def getPriority(self):
		return 50

class serialConnection(printerConnectionBase.printerConnectionBase):
	"""
	A serial connection. Needs to build an active-connection.
	When an active connection is created, a 2nd python process is spawned which handles the actual serial communication.

	This class communicates with the Cura.serialCommunication module trough stdin/stdout pipes.
	"""
	def __init__(self, port):
		super(serialConnection, self).__init__(port)
		self._portName = port

		self._process = None
		self._thread = None

		self._temperature = []
		self._targetTemperature = []
		self._bedTemperature = 0
		self._targetBedTemperature = 0
		self._log = []

		self._commState = None
		self._commStateString = None
		self._gcodeData = []

	#Load the data into memory for printing, returns True on success
	def loadGCodeData(self, dataStream):
		if self.isPrinting() is None:
			return False
		self._gcodeData = []
		for line in dataStream:
			#Strip out comments, we do not need to send comments
			if ';' in line:
				line = line[:line.index(';')]
			#Strip out whitespace at the beginning/end this saves data to send.
			line = line.strip()

			if len(line) < 1:
				continue
			self._gcodeData.append(line)
		return True

	#Start printing the previously loaded file
	def startPrint(self):
		if self.isPrinting() or len(self._gcodeData) < 1 or self._process is None:
			return
		self._process.stdin.write('STOP\n')
		for line in self._gcodeData:
			self._process.stdin.write('G:%s\n' % (line))
		self._process.stdin.write('START\n')
		self._printProgress = 0

	def coolDown(self):
		cooldown_toolhead = "M104 S0"
		for i in range(0,3):
			change_toolhead = "T%d".format(i)
			self.sendCommand(change_toolhead)
			self.sendCommand(cooldown_toolhead)
		self.sendCommand("M140 S0") #Bed

	def disableSteppers(self):
		self.sendCommand("M18")

	#Abort the previously loaded print file
	def cancelPrint(self):
		if not self.isPrinting() or self._process is None:
			return
		self._process.stdin.write('STOP\n')
		self._printProgress = 0
		self.coolDown()
		self.disableSteppers()

	def isPrinting(self):
		return self._commState == machineCom.MachineCom.STATE_PRINTING or self.isPaused()

	#Returns true if we have the ability to pause the file printing.
	def hasPause(self):
		return True

	def isPaused(self):
		return self._commState == machineCom.MachineCom.STATE_PAUSED

	#Pause or unpause the printing depending on the value, if supported.
	def pause(self, value):
		if not self.isPrinting() or self._process is None:
			return
		self._process.stdin.write('PAUSE\n' if value else "RESUME\n")

	#Amount of progression of the current print file. 0.0 to 1.0
	def getPrintProgress(self):
		if len(self._gcodeData) < 1:
			return 0.0
		return float(self._printProgress) / float(len(self._gcodeData))

	# Return if the printer with this connection type is available
	def isAvailable(self):
		return True

	# Get the connection status string. This is displayed to the user and can be used to communicate
	#  various information to the user.
	def getStatusString(self):
		return "%s" % (self._commStateString)

	#Returns true if we need to establish an active connection. True for serial connections.
	def hasActiveConnection(self):
		return True

	#Open the active connection to the printer so we can send commands
	def openActiveConnection(self):
		self.closeActiveConnection()
		self._thread = threading.Thread(target=self._serialCommunicationThread)
		self._thread.daemon = True
		self._thread.start()

	#Close the active connection to the printer
	def closeActiveConnection(self):
		if self._process is not None:
			self._process.terminate()
			self._thread.join()

	#Is the active connection open right now.
	def isActiveConnectionOpen(self):
		if self._process is None:
			return False
		return self._commState == machineCom.MachineCom.STATE_OPERATIONAL or self._commState == machineCom.MachineCom.STATE_PRINTING or self._commState == machineCom.MachineCom.STATE_PAUSED

	#Are we trying to open an active connection right now.
	def isActiveConnectionOpening(self):
		if self._process is None:
			return False
		return self._commState == machineCom.MachineCom.STATE_OPEN_SERIAL or self._commState == machineCom.MachineCom.STATE_CONNECTING or self._commState == machineCom.MachineCom.STATE_DETECT_SERIAL or self._commState == machineCom.MachineCom.STATE_DETECT_BAUDRATE

	def getTemperature(self, extruder):
		if extruder >= len(self._temperature):
			return None
		return self._temperature[extruder]

	def getBedTemperature(self):
		return self._bedTemperature

	#Are we able to send a direct command with sendCommand at this moment in time.
	def isAbleToSendDirectCommand(self):
		return self.isActiveConnectionOpen()

	#Directly send a command to the printer.
	def sendCommand(self, command):
		if self._process is None:
			return
		self._process.stdin.write('C:%s\n' % (command))

	#Returns true if we got some kind of error. The getErrorLog returns all the information to diagnose the problem.
	def isInErrorState(self):
		return self._commState == machineCom.MachineCom.STATE_ERROR or self._commState == machineCom.MachineCom.STATE_CLOSED_WITH_ERROR

	#Returns the error log in case there was an error.
	def getErrorLog(self):
		return '\n'.join(self._log)

	def _serialCommunicationThread(self):
		if platform.system() == "Darwin" and hasattr(sys, 'frozen'):
			cmdList = [os.path.join(os.path.dirname(sys.executable), 'Cura'), '--serialCommunication']
			cmdList += [self._portName + ':' + profile.getMachineSetting('serial_baud')]
		else:
			cmdList = [sys.executable, '-m', 'Cura.serialCommunication']
			cmdList += [self._portName, profile.getMachineSetting('serial_baud')]
		if platform.system() == "Darwin":
			if platform.machine() == 'i386':
				cmdList = ['arch', '-i386'] + cmdList
		self._process = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
		line = self._process.stdout.readline()
		while len(line) > 0:
			line = line.strip()
			line = line.split(':', 1)
			if line[0] == '':
				pass
			elif line[0] == 'log':
				self._log.append(line[1])
				if len(self._log) > 30:
					self._log.pop(0)
			elif line[0] == 'temp':
				line = line[1].split(':')
				self._temperature = json.loads(line[0])
				self._targetTemperature = json.loads(line[1])
				self._bedTemperature = float(line[2])
				self._targetBedTemperature = float(line[3])
				self._doCallback()
			elif line[0] == 'message':
				self._doCallback(line[1])
			elif line[0] == 'state':
				line = line[1].split(':', 1)
				self._commState = int(line[0])
				self._commStateString = line[1]
				self._doCallback('')
			elif line[0] == 'progress':
				self._printProgress = int(line[1])
				self._doCallback()
			else:
				print line
			line = self._process.stdout.readline()
		self._process = None
<commit_msg>Restore commit fda889d91263c6c381334e4f7a6d1bbb687a8e3f<commit_after>"""
The serial/USB printer connection. Uses a 2nd python process to connect to the printer so we never
have locking problems where other threads in python can block the USB printing.
"""
__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"

import threading
import time
import platform
import os
import sys
import subprocess
import json

from Cura.util import profile
from Cura.util import machineCom
from Cura.util.printerConnection import printerConnectionBase

class serialConnectionGroup(printerConnectionBase.printerConnectionGroup):
	"""
	The serial connection group. Keeps track of all available serial ports,
	and builds a serialConnection for each port.
	"""
	def __init__(self):
		super(serialConnectionGroup, self).__init__("USB")
		self._connectionMap = {}

	def getAvailableConnections(self):
		if profile.getMachineSetting('serial_port') == 'AUTO':
			serialList = machineCom.serialList(True)
		else:
			serialList = [profile.getMachineSetting('serial_port')]
		for port in serialList:
			if port not in self._connectionMap:
				self._connectionMap[port] = serialConnection(port)
		for key in self._connectionMap.keys():
			if key not in serialList and not self._connectionMap[key].isActiveConnectionOpen():
				self._connectionMap.pop(key)
		return self._connectionMap.values()

	def getIconID(self):
		return 6

	def getPriority(self):
		return 50

class serialConnection(printerConnectionBase.printerConnectionBase):
	"""
	A serial connection. Needs to build an active-connection.
	When an active connection is created, a 2nd python process is spawned which handles the actual serial communication.

	This class communicates with the Cura.serialCommunication module trough stdin/stdout pipes.
	"""
	def __init__(self, port):
		super(serialConnection, self).__init__(port)
		self._portName = port

		self._process = None
		self._thread = None

		self._temperature = []
		self._targetTemperature = []
		self._bedTemperature = 0
		self._targetBedTemperature = 0
		self._log = []

		self._commState = None
		self._commStateString = None
		self._gcodeData = []

	#Load the data into memory for printing, returns True on success
	def loadGCodeData(self, dataStream):
		if self.isPrinting() is None:
			return False
		self._gcodeData = []
		for line in dataStream:
			#Strip out comments, we do not need to send comments
			if ';' in line:
				line = line[:line.index(';')]
			#Strip out whitespace at the beginning/end this saves data to send.
			line = line.strip()

			if len(line) < 1:
				continue
			self._gcodeData.append(line)
		return True

	#Start printing the previously loaded file
	def startPrint(self):
		if self.isPrinting() or len(self._gcodeData) < 1 or self._process is None:
			return
		self._process.stdin.write('STOP\n')
		for line in self._gcodeData:
			self._process.stdin.write('G:%s\n' % (line))
		self._process.stdin.write('START\n')
		self._printProgress = 0

	def coolDown(self):
		cooldown_toolhead = "M104 S0"
		for i in range(0,3):
			change_toolhead = "T%d".format(i)
			self.sendCommand(change_toolhead)
			self.sendCommand(cooldown_toolhead)
		self.sendCommand("M140 S0") #Bed

	def disableSteppers(self):
		self.sendCommand("M18")

	#Abort the previously loaded print file
	def cancelPrint(self):
		if not self.isPrinting() or self._process is None:
			return
		self._process.stdin.write('STOP\n')
		self._printProgress = 0
		self.coolDown()
		self.disableSteppers()

	def isPrinting(self):
		return self._commState == machineCom.MachineCom.STATE_PRINTING

	#Returns true if we have the ability to pause the file printing.
	def hasPause(self):
		return True

	def isPaused(self):
		return self._commState == machineCom.MachineCom.STATE_PAUSED

	#Pause or unpause the printing depending on the value, if supported.
	def pause(self, value):
		if not (self.isPrinting() or self.isPaused()) or self._process is None:
			return
		self._process.stdin.write('PAUSE\n' if value else "RESUME\n")

	#Amount of progression of the current print file. 0.0 to 1.0
	def getPrintProgress(self):
		if len(self._gcodeData) < 1:
			return 0.0
		return float(self._printProgress) / float(len(self._gcodeData))

	# Return if the printer with this connection type is available
	def isAvailable(self):
		return True

	# Get the connection status string. This is displayed to the user and can be used to communicate
	#  various information to the user.
	def getStatusString(self):
		return "%s" % (self._commStateString)

	#Returns true if we need to establish an active connection. True for serial connections.
	def hasActiveConnection(self):
		return True

	#Open the active connection to the printer so we can send commands
	def openActiveConnection(self):
		self.closeActiveConnection()
		self._thread = threading.Thread(target=self._serialCommunicationThread)
		self._thread.daemon = True
		self._thread.start()

	#Close the active connection to the printer
	def closeActiveConnection(self):
		if self._process is not None:
			self._process.terminate()
			self._thread.join()

	#Is the active connection open right now.
	def isActiveConnectionOpen(self):
		if self._process is None:
			return False
		return self._commState == machineCom.MachineCom.STATE_OPERATIONAL or self._commState == machineCom.MachineCom.STATE_PRINTING or self._commState == machineCom.MachineCom.STATE_PAUSED

	#Are we trying to open an active connection right now.
	def isActiveConnectionOpening(self):
		if self._process is None:
			return False
		return self._commState == machineCom.MachineCom.STATE_OPEN_SERIAL or self._commState == machineCom.MachineCom.STATE_CONNECTING or self._commState == machineCom.MachineCom.STATE_DETECT_SERIAL or self._commState == machineCom.MachineCom.STATE_DETECT_BAUDRATE

	def getTemperature(self, extruder):
		if extruder >= len(self._temperature):
			return None
		return self._temperature[extruder]

	def getBedTemperature(self):
		return self._bedTemperature

	#Are we able to send a direct command with sendCommand at this moment in time.
	def isAbleToSendDirectCommand(self):
		return self.isActiveConnectionOpen()

	#Directly send a command to the printer.
	def sendCommand(self, command):
		if self._process is None:
			return
		self._process.stdin.write('C:%s\n' % (command))

	#Returns true if we got some kind of error. The getErrorLog returns all the information to diagnose the problem.
	def isInErrorState(self):
		return self._commState == machineCom.MachineCom.STATE_ERROR or self._commState == machineCom.MachineCom.STATE_CLOSED_WITH_ERROR

	#Returns the error log in case there was an error.
	def getErrorLog(self):
		return '\n'.join(self._log)

	def _serialCommunicationThread(self):
		if platform.system() == "Darwin" and hasattr(sys, 'frozen'):
			cmdList = [os.path.join(os.path.dirname(sys.executable), 'Cura'), '--serialCommunication']
			cmdList += [self._portName + ':' + profile.getMachineSetting('serial_baud')]
		else:
			cmdList = [sys.executable, '-m', 'Cura.serialCommunication']
			cmdList += [self._portName, profile.getMachineSetting('serial_baud')]
		if platform.system() == "Darwin":
			if platform.machine() == 'i386':
				cmdList = ['arch', '-i386'] + cmdList
		self._process = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
		line = self._process.stdout.readline()
		while len(line) > 0:
			line = line.strip()
			line = line.split(':', 1)
			if line[0] == '':
				pass
			elif line[0] == 'log':
				self._log.append(line[1])
				if len(self._log) > 30:
					self._log.pop(0)
			elif line[0] == 'temp':
				line = line[1].split(':')
				self._temperature = json.loads(line[0])
				self._targetTemperature = json.loads(line[1])
				self._bedTemperature = float(line[2])
				self._targetBedTemperature = float(line[3])
				self._doCallback()
			elif line[0] == 'message':
				self._doCallback(line[1])
			elif line[0] == 'state':
				line = line[1].split(':', 1)
				self._commState = int(line[0])
				self._commStateString = line[1]
				self._doCallback('')
			elif line[0] == 'progress':
				self._printProgress = int(line[1])
				self._doCallback()
			else:
				print line
			line = self._process.stdout.readline()
		self._process = None
<|endoftext|>


<commit_before>import os
import sys
import cherrypy
import cfg
import sqlite3

from django.template.loader import render_to_string
from filedict import FileDict

def mkdir(newdir):
   """works the way a good mkdir should :)
        - already exists, silently complete
        - regular file in the way, raise an exception
        - parent directory(ies) does not exist, make them as well
   """
   if os.path.isdir(newdir):
      pass
   elif os.path.isfile(newdir):
      raise OSError("a file with the same name as the desired " \
                      "dir, '%s', already exists." % newdir)
   else:
      head, tail = os.path.split(newdir)
      if head and not os.path.isdir(head):
         mkdir(head)
      #print "mkdir %s" % repr(newdir)
      if tail:
         os.mkdir(newdir)
def is_string(obj):
    isinstance(obj, basestring)
def is_ascii(s):
    return all(ord(c) < 128 for c in s)
def is_alphanumeric(string):
    for c in string:
        o = ord(c)
        if not o in range(48, 58) + range(41, 91) + [95] + range(97, 123):
            return False
    return True

def slurp(filespec):
    with open(filespec) as x: f = x.read()
    return f

def unslurp(filespec, msg):
    with open(filespec, 'w') as x:
        x.write(msg)

def find_in_seq(func, seq):
  "Return first item in seq for which func(item) returns True."
  for i in seq:
    if func(i):
      return i

def find_keys(dic, val):
    """return the key of dictionary dic given the value"""
    return [k for k, v in dic.iteritems() if v == val]

class Message():
    def __init__(self, msg=''):
        self.text = msg
    def add(self, text):
        self.text += "<br />%s" % text

def render_template(template='login_nav', **kwargs):
    for key in ['sidebar_left', 'sidebar_right', 'main', 'js', 'nav', 'css',
                'title', 'basehref']:
        if not key in kwargs:
            kwargs[key] = ''

    if kwargs['basehref'] == '':
        kwargs['basehref'] = cfg.server_dir

    kwargs['template'] = template
    kwargs['main_menu'] = cfg.main_menu
    kwargs['submenu'] = cfg.main_menu.active_item()
    kwargs['current_url'] = cherrypy.url()
    kwargs['username'] = cherrypy.session.get(cfg.session_key)
    kwargs['cfg'] = cfg

    return render_to_string(template + '.html', kwargs)

def filedict_con(filespec=None, table='dict'):
    """TODO: better error handling in filedict_con"""
    try:
        return FileDict(connection=sqlite3.connect(filespec), table=table)
    except IOError as (errno, strerror):
        cfg.log.critical("I/O error({0}): {1}".format(errno, strerror))
        sys.exit(-1)
<commit_msg>Remove unused Message utility class<commit_after>import os
import sys
import cherrypy
import cfg
import sqlite3

from django.template.loader import render_to_string
from filedict import FileDict

def mkdir(newdir):
   """works the way a good mkdir should :)
        - already exists, silently complete
        - regular file in the way, raise an exception
        - parent directory(ies) does not exist, make them as well
   """
   if os.path.isdir(newdir):
      pass
   elif os.path.isfile(newdir):
      raise OSError("a file with the same name as the desired " \
                      "dir, '%s', already exists." % newdir)
   else:
      head, tail = os.path.split(newdir)
      if head and not os.path.isdir(head):
         mkdir(head)
      #print "mkdir %s" % repr(newdir)
      if tail:
         os.mkdir(newdir)
def is_string(obj):
    isinstance(obj, basestring)
def is_ascii(s):
    return all(ord(c) < 128 for c in s)
def is_alphanumeric(string):
    for c in string:
        o = ord(c)
        if not o in range(48, 58) + range(41, 91) + [95] + range(97, 123):
            return False
    return True

def slurp(filespec):
    with open(filespec) as x: f = x.read()
    return f

def unslurp(filespec, msg):
    with open(filespec, 'w') as x:
        x.write(msg)

def find_in_seq(func, seq):
  "Return first item in seq for which func(item) returns True."
  for i in seq:
    if func(i):
      return i

def find_keys(dic, val):
    """return the key of dictionary dic given the value"""
    return [k for k, v in dic.iteritems() if v == val]

def render_template(template='login_nav', **kwargs):
    for key in ['sidebar_left', 'sidebar_right', 'main', 'js', 'nav', 'css',
                'title', 'basehref']:
        if not key in kwargs:
            kwargs[key] = ''

    if kwargs['basehref'] == '':
        kwargs['basehref'] = cfg.server_dir

    kwargs['template'] = template
    kwargs['main_menu'] = cfg.main_menu
    kwargs['submenu'] = cfg.main_menu.active_item()
    kwargs['current_url'] = cherrypy.url()
    kwargs['username'] = cherrypy.session.get(cfg.session_key)
    kwargs['cfg'] = cfg

    return render_to_string(template + '.html', kwargs)

def filedict_con(filespec=None, table='dict'):
    """TODO: better error handling in filedict_con"""
    try:
        return FileDict(connection=sqlite3.connect(filespec), table=table)
    except IOError as (errno, strerror):
        cfg.log.critical("I/O error({0}): {1}".format(errno, strerror))
        sys.exit(-1)
<|endoftext|>