<commit_before>from parser.lines import path, group
from parser import line
import copy, collections

class InvalidSubgraphError (Exception): pass

class Subgraph:

    def __init__ (self, graph_id, elements, opt_fields={}):
        if not isinstance (graph_id, str):
            raise InvalidSubgraphError ("A  has always an id of type string, " + \
                                 "given {0} of type {1}".format (node_id, type (node_id)))

        if not isinstance (elements, dict):
            raise InvalidSubgraphError ("A dictionary of elements id:orientation is required.")

        self._sub_id = graph_id
        self._elements = elements
        self._opt_fields = {}
        for key, field in opt_fields.items ():
            if line.is_field (field):
                self._opt_fields[key] = copy.deepcopy (field)

    @property
    def sub_id (self):
        return self._sub_id

    @property
    def elements (self):
        return self._elements

    @property
    def opt_fields (self):
        return self._opt_fields

    @classmethod
    def from_line (cls, line):

        if not line.is_valid ():
            raise InvalidSubgraphError ("The line to be added must have all the required fields. Line type: '{0}'".format (line.type))

        try:
            fields = copy.deepcopy (line.fields)

            if line.type == 'P':
                fields.pop ('path_name')
                fields.pop ('seqs_names')

                return Subgraph ( \
                                line.fields['path_name'].value, \
                                collections.OrderedDict ((ref[0:-1], ref[-1:]) for ref in line.fields['seqs_names'].value), \
                                fields)

            if line.type == 'O':
                fields.pop ('oid')
                fields.pop ('references')

                return Subgraph ( \
                                line.fields['oid'].value, \
                                collections.OrderedDict ((ref[0:-1], ref[-1:]) for ref in line.fields['references'].value), \
                                fields)

            if line.type == 'U':
                fields.pop ('uid')
                fields.pop ('ids')

                return Subgraph ( \
                                line.fields['uid'].value, \
                                collections.OrderedDict ((id,None) for id in line.fields['ids'].value), \
                                fields)

        except Exception as e:
            raise e

    def __str__ (self):
        return str.join(",\t", [ \
                                     self.sub_id, \
                                     str.join ("\t", [id+orn for id, orn in self.elements.items()]), \
                                     str.join ("\t", [str(field) + ": " + str(item) \
                                               for field, item in self.opt_fields.items ()])\
                                ])

<commit_msg>Added as_dict method<commit_after>from parser.lines import path, group
from parser import line
import copy, collections

class InvalidSubgraphError (Exception): pass

class Subgraph:

    def __init__ (self, graph_id, elements, opt_fields={}):
        if not isinstance (graph_id, str):
            raise InvalidSubgraphError ("A  has always an id of type string, " + \
                                 "given {0} of type {1}".format (node_id, type (node_id)))

        if not isinstance (elements, dict):
            raise InvalidSubgraphError ("A dictionary of elements id:orientation is required.")

        self._sub_id = graph_id
        self._elements = elements
        self._opt_fields = {}
        for key, field in opt_fields.items ():
            if line.is_field (field):
                self._opt_fields[key] = copy.deepcopy (field)

    @property
    def sub_id (self):
        return self._sub_id

    @property
    def elements (self):
        return self._elements

    @property
    def opt_fields (self):
        return self._opt_fields

    @classmethod
    def from_line (cls, line):

        if not line.is_valid ():
            raise InvalidSubgraphError ("The line to be added must have all the required fields. Line type: '{0}'".format (line.type))

        try:
            fields = copy.deepcopy (line.fields)

            if line.type == 'P':
                fields.pop ('path_name')
                fields.pop ('seqs_names')

                return Subgraph ( \
                                line.fields['path_name'].value, \
                                collections.OrderedDict ((ref[0:-1], ref[-1:]) for ref in line.fields['seqs_names'].value), \
                                fields)

            if line.type == 'O':
                fields.pop ('oid')
                fields.pop ('references')

                return Subgraph ( \
                                line.fields['oid'].value, \
                                collections.OrderedDict ((ref[0:-1], ref[-1:]) for ref in line.fields['references'].value), \
                                fields)

            if line.type == 'U':
                fields.pop ('uid')
                fields.pop ('ids')

                return Subgraph ( \
                                line.fields['uid'].value, \
                                collections.OrderedDict ((id,None) for id in line.fields['ids'].value), \
                                fields)

        except Exception as e:
            raise e

    def as_dict (self):
        """!
        A custom method to get all the method fields and the optional fields
        into a dictionary.
        """

        retval = {}
        retval['sub_id'] = self.sub_id
        retval['elements'] =  self.elements

        for key, value in self.opt_fields:
            retval[key] = value

        return retval

    def __str__ (self):
        return str.join(",\t", [ \
                                     self.sub_id, \
                                     str.join ("\t", [id+(orn if orn != None else "") for id, orn in self.elements.items()]), \
                                     str.join ("\t", [str(field) + ": " + str(item) \
                                               for field, item in self.opt_fields.items ()])\
                                ])

<|endoftext|>


<commit_before>#!/usr/bin/env python

import sys
import os
import shutil
from os.path import join, dirname, basename, abspath
from optparse import OptionParser

builddir = abspath(dirname(dirname(__file__)))
sys.path.append(join(builddir, "pyjs"))
import pyjs

usage = """
  usage: %prog [options] <application name>

This is the command line builder for the pyjamas project, which can be used to
build Ajax applications from Python.
For more information, see the website at http://pyjamas.pyworks.org/
"""

# GWT1.2 Impl  | GWT1.2 Output         | Pyjamas 0.2 Platform | Pyjamas 0.2 Output
# -------------+-----------------------+----------------------+----------------------
# IE6          | ie6                   | IE6                  | ie6
# Opera        | opera                 | Opera                | opera
# Safari       | safari                | Safari               | safari
# --           | gecko1_8              | Mozilla              | mozilla
# --           | gecko                 | OldMoz               | oldmoz
# Standard     | all                   | (default code)       | all
# Mozilla      | gecko1_8, gecko       | --                   | --
# Old          | safari, gecko, opera  | --                   | --

version = "%prog pyjamas version 2006-08-19"
app_platforms = ['IE6', 'Opera', 'OldMoz', 'Safari', 'Mozilla']
app_library_dirs = [
            join(builddir, "library/builtins"),
            join(builddir, "library"),
            join(builddir, "addons")]

def read_boilerplate(filename):
    return open(join(dirname(__file__), "boilerplate", filename)).read()

def copy_boilerplate(filename, output_dir):
    filename = join(dirname(__file__), "boilerplate", filename)
    shutil.copy(filename, output_dir)

# taken and modified from python2.4
def copytree_exists(src, dst, symlinks=False):
    if not os.path.exists(src):
        return

    names = os.listdir(src)
    try:
        os.mkdir(dst)
    except:
        pass

    errors = []
    for name in names:
        if name.startswith('.svn'):
            continue

        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                copytree_exists(srcname, dstname, symlinks)
            else:
                shutil.copy2(srcname, dstname)
        except (IOError, os.error), why:
            errors.append((srcname, dstname, why))
    if errors:
        print errors

def build(app_name, output="output", js_includes=(), debug=False):
    dir_public = "public"

    # make sure the output directory is always created in the current working
    # directory or at the place given if it is an absolute path.
    output = os.path.abspath(output)

    msg = "Building '%(app_name)s' to output directory '%(output)s'" % locals()
    if debug:
        msg += " with debugging statements"
    print msg

    # check the output directory
    if os.path.exists(output) and not os.path.isdir(output):
        print >>sys.stderr, "Output destination %s exists and is not a directory" % output
        return
    if not os.path.isdir(output):
        try:
            print "Creating output directory"
            os.mkdir(output)
        except StandardError, e:
            print >>sys.stderr, "Exception creating output directory %s: %s" % (output, e)

    # Check that the app_name file exists
    py_app_name = app_name[:]
    if py_app_name[-2:] != "py":
        py_app_name = app_name + ".py"
    if app_name[-3:] == ".py":
        app_name = app_name[:-3]
    app_basename = basename(app_name)

    if not os.path.isfile(py_app_name):
        print >>sys.stderr, "Could not find %s" % py_app_name
        return

    # make the app directory the working directory to make relative imports
    # work if the directory from where build is started is not the same as the
    # app directory.
    app_dir = dirname(app_name)
    if app_dir:
        os.chdir(app_dir)

    ## public dir
    print "Copying: public directory"
    copytree_exists(dir_public, output)

    ## AppName.html - can be in current or public directory
    html_input_filename = app_name + ".html"
    html_output_filename = join(output, basename(html_input_filename))
    if os.path.isfile(html_input_filename):
        if not os.path.isfile(html_output_filename) or os.path.getmtime(html_input_filename) > os.path.getmtime(html_output_filename):
            try:
                shutil.copy(html_input_filename, html_output_filename)
            except:
                print >>sys.stderr, "Warning: Missing module HTML file %s" % html_input_filename

            print "Copying: %(html_input_filename)s" % locals()

    ## pygwt.js

    print "Copying: pygwt.js"

    pygwt_js_template = read_boilerplate("pygwt.js")
    pygwt_js_output = open(join(output, "pygwt.js"), "w")

    print >>pygwt_js_output, pygwt_js_template

    pygwt_js_output.close()

    ## Images

    print "Copying: Images and History"
    copy_boilerplate("corner_dialog_topleft_black.png", output)
    copy_boilerplate("corner_dialog_topright_black.png", output)
    copy_boilerplate("corner_dialog_bottomright_black.png", output)
    copy_boilerplate("corner_dialog_bottomleft_black.png", output)
    copy_boilerplate("corner_dialog_edge_black.png", output)
    copy_boilerplate("corner_dialog_topleft.png", output)
    copy_boilerplate("corner_dialog_topright.png", output)
    copy_boilerplate("corner_dialog_bottomright.png", output)
    copy_boilerplate("corner_dialog_bottomleft.png", output)
    copy_boilerplate("corner_dialog_edge.png", output)
    copy_boilerplate("tree_closed.gif", output)
    copy_boilerplate("tree_open.gif", output)
    copy_boilerplate("tree_white.gif", output)
    copy_boilerplate("history.html", output)

    ## AppName.nocache.html

    print "Creating: %(app_basename)s.nocache.html" % locals()

    home_nocache_html_template = read_boilerplate("home.nocache.html")
    home_nocache_html_output = open(join(output, app_basename + ".nocache.html"), "w")

    print >>home_nocache_html_output, home_nocache_html_template % dict(
        app_name = app_basename,
        safari_js = "%s.Safari" % app_basename,
        ie6_js = "%s.IE6" % app_basename,
        oldmoz_js = "%s.OldMoz" % app_basename,
        moz_js = "%s.Mozilla" % app_basename,
        opera_js = "%s.Opera" % app_basename,
    )

    home_nocache_html_output.close()

    ## all.cache.html

    all_cache_html_template = read_boilerplate("all.cache.html")

    parser = pyjs.PlatformParser("platform")
    app_headers = ''
    app_body = '\n'.join(['<script type="text/javascript" src="%s"></script>'%script for script in js_includes])

    for platform in app_platforms:
        all_cache_name = "%s.%s.cache.html" % (app_basename, platform)
        print "Creating: " + all_cache_name

        parser.setPlatform(platform)
        app_translator = pyjs.AppTranslator(app_library_dirs, parser)
        app_libs = app_translator.translateLibraries(['pyjslib'], debug)
        app_code = app_translator.translate(app_name, debug=debug)
        all_cache_html_output = open(join(output, all_cache_name), "w")

        print >>all_cache_html_output, all_cache_html_template % dict(
            app_name = app_basename,
            app_libs = app_libs,
            app_code = app_code,
            app_body = app_body,
            app_headers = app_headers
        )

        all_cache_html_output.close()

    ## Done.

    print "Done. You can run your app by opening '%(html_output_filename)s' in a browser" % locals()

def main():
    global app_library_dirs
    global app_platforms

    parser = OptionParser(usage = usage, version = version)
    parser.add_option("-o", "--output", dest="output",
        help="directory to which the webapp should be written")
    parser.add_option("-j", "--include-js", dest="js_includes", action="append",
        help="javascripts to load into the same frame as the rest of the script")
    parser.add_option("-I", "--library_dir", dest="library_dirs", action="append",
        help="paths to search for python modules")
    parser.add_option("-P", "--platforms", dest="platforms",
        help="platforms to build for, comma-seperated")
    parser.add_option("-d", "--debug", action="store_true", dest="debug")

    parser.set_defaults(output = "output", js_includes=[], library_dirs=[], platforms=(','.join(app_platforms)), debug=False)
    (options, args) = parser.parse_args()
    if len(args) != 1:
        parser.error("incorrect number of arguments")

    app_library_dirs += options.library_dirs
    if options.platforms:
       app_platforms = options.platforms.split(',')
    build(args[0], options.output, options.js_includes, options.debug)

if __name__ == "__main__":
    main()

<commit_msg>make path handling absolute throughout the builder<commit_after>#!/usr/bin/env python

import sys
import os
import shutil
from os.path import join, dirname, basename, abspath, split, isfile
from optparse import OptionParser

builddir = abspath(dirname(dirname(__file__)))
sys.path.append(join(builddir, "pyjs"))
import pyjs

usage = """
  usage: %prog [options] <application module name or path>

This is the command line builder for the pyjamas project, which can be used to
build Ajax applications from Python.
For more information, see the website at http://pyjamas.pyworks.org/
"""

# GWT1.2 Impl  | GWT1.2 Output         | Pyjamas 0.2 Platform | Pyjamas 0.2 Output
# -------------+-----------------------+----------------------+----------------------
# IE6          | ie6                   | IE6                  | ie6
# Opera        | opera                 | Opera                | opera
# Safari       | safari                | Safari               | safari
# --           | gecko1_8              | Mozilla              | mozilla
# --           | gecko                 | OldMoz               | oldmoz
# Standard     | all                   | (default code)       | all
# Mozilla      | gecko1_8, gecko       | --                   | --
# Old          | safari, gecko, opera  | --                   | --

version = "%prog pyjamas version 2006-08-19"
app_platforms = ['IE6', 'Opera', 'OldMoz', 'Safari', 'Mozilla']
app_library_dirs = [
            join(builddir, "library/builtins"),
            join(builddir, "library"),
            join(builddir, "addons")]

def read_boilerplate(filename):
    return open(join(dirname(__file__), "boilerplate", filename)).read()

def copy_boilerplate(filename, output_dir):
    filename = join(dirname(__file__), "boilerplate", filename)
    shutil.copy(filename, output_dir)

# taken and modified from python2.4
def copytree_exists(src, dst, symlinks=False):
    if not os.path.exists(src):
        return

    names = os.listdir(src)
    try:
        os.mkdir(dst)
    except:
        pass

    errors = []
    for name in names:
        if name.startswith('.svn'):
            continue

        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                copytree_exists(srcname, dstname, symlinks)
            else:
                shutil.copy2(srcname, dstname)
        except (IOError, os.error), why:
            errors.append((srcname, dstname, why))
    if errors:
        print errors

def build(app_name, output="output", js_includes=(), debug=False):
    dir_public = "public"

    # make sure the output directory is always created in the current working
    # directory or at the place given if it is an absolute path.
    output = os.path.abspath(output)

    msg = "Building '%(app_name)s' to output directory '%(output)s'" % locals()
    if debug:
        msg += " with debugging statements"
    print msg

    # check the output directory
    if os.path.exists(output) and not os.path.isdir(output):
        print >>sys.stderr, "Output destination %s exists and is not a directory" % output
        return
    if not os.path.isdir(output):
        try:
            print "Creating output directory"
            os.mkdir(output)
        except StandardError, e:
            print >>sys.stderr, "Exception creating output directory %s: %s" % (output, e)

    ## public dir
    print "Copying: public directory"
    copytree_exists(dir_public, output)

    ## AppName.html - can be in current or public directory
    html_input_filename = app_name + ".html"
    html_output_filename = join(output, basename(html_input_filename))
    if os.path.isfile(html_input_filename):
        if not os.path.isfile(html_output_filename) or os.path.getmtime(html_input_filename) > os.path.getmtime(html_output_filename):
            try:
                shutil.copy(html_input_filename, html_output_filename)
            except:
                print >>sys.stderr, "Warning: Missing module HTML file %s" % html_input_filename

            print "Copying: %(html_input_filename)s" % locals()

    ## pygwt.js

    print "Copying: pygwt.js"

    pygwt_js_template = read_boilerplate("pygwt.js")
    pygwt_js_output = open(join(output, "pygwt.js"), "w")

    print >>pygwt_js_output, pygwt_js_template

    pygwt_js_output.close()

    ## Images

    print "Copying: Images and History"
    copy_boilerplate("corner_dialog_topleft_black.png", output)
    copy_boilerplate("corner_dialog_topright_black.png", output)
    copy_boilerplate("corner_dialog_bottomright_black.png", output)
    copy_boilerplate("corner_dialog_bottomleft_black.png", output)
    copy_boilerplate("corner_dialog_edge_black.png", output)
    copy_boilerplate("corner_dialog_topleft.png", output)
    copy_boilerplate("corner_dialog_topright.png", output)
    copy_boilerplate("corner_dialog_bottomright.png", output)
    copy_boilerplate("corner_dialog_bottomleft.png", output)
    copy_boilerplate("corner_dialog_edge.png", output)
    copy_boilerplate("tree_closed.gif", output)
    copy_boilerplate("tree_open.gif", output)
    copy_boilerplate("tree_white.gif", output)
    copy_boilerplate("history.html", output)

    ## AppName.nocache.html

    print "Creating: %(app_name)s.nocache.html" % locals()

    home_nocache_html_template = read_boilerplate("home.nocache.html")
    home_nocache_html_output = open(join(output, app_name + ".nocache.html"), "w")

    print >>home_nocache_html_output, home_nocache_html_template % dict(
        app_name = app_name,
        safari_js = "%s.Safari" % app_name,
        ie6_js = "%s.IE6" % app_name,
        oldmoz_js = "%s.OldMoz" % app_name,
        moz_js = "%s.Mozilla" % app_name,
        opera_js = "%s.Opera" % app_name,
    )

    home_nocache_html_output.close()

    ## all.cache.html

    all_cache_html_template = read_boilerplate("all.cache.html")

    parser = pyjs.PlatformParser("platform")
    app_headers = ''
    app_body = '\n'.join(['<script type="text/javascript" src="%s"></script>'%script for script in js_includes])

    for platform in app_platforms:
        all_cache_name = "%s.%s.cache.html" % (app_name, platform)
        print "Creating: " + all_cache_name

        parser.setPlatform(platform)
        app_translator = pyjs.AppTranslator(app_library_dirs, parser)
        app_libs = app_translator.translateLibraries(['pyjslib'], debug)
        app_code = app_translator.translate(app_name, debug=debug)
        all_cache_html_output = open(join(output, all_cache_name), "w")

        print >>all_cache_html_output, all_cache_html_template % dict(
            app_name = app_name,
            app_libs = app_libs,
            app_code = app_code,
            app_body = app_body,
            app_headers = app_headers
        )

        all_cache_html_output.close()

    ## Done.

    print "Done. You can run your app by opening '%(html_output_filename)s' in a browser" % locals()

def main():
    global app_library_dirs
    global app_platforms

    parser = OptionParser(usage = usage, version = version)
    parser.add_option("-o", "--output", dest="output",
        help="directory to which the webapp should be written")
    parser.add_option("-j", "--include-js", dest="js_includes", action="append",
        help="javascripts to load into the same frame as the rest of the script")
    parser.add_option("-I", "--library_dir", dest="library_dirs", action="append",
        help="paths to search for python modules")
    parser.add_option("-P", "--platforms", dest="platforms",
        help="platforms to build for, comma-seperated")
    parser.add_option("-d", "--debug", action="store_true", dest="debug")

    parser.set_defaults(output = "output", js_includes=[],
                        library_dirs=[],
                        platforms=(','.join(app_platforms)), debug=False)
    (options, args) = parser.parse_args()
    if len(args) != 1:
        parser.error("incorrect number of arguments")

    lib_dirs = set()
    app_path = args[0]
    if app_path.endswith('.py'):
        app_path = abspath(app_path)
        if not isfile(app_path):
            parser.error("Application file not found %r" % app_path)
        app_path, app_name = split(app_path)
        app_name = app_name[:-3]
        lib_dirs.add(app_path)
    elif os.path.sep in app_path:
        parser.error("Not a valid module declaration %r" % app_path)
    else:
        app_name = app_path

    for d in options.library_dirs:
        lib_dirs.add(abspath(d))
    app_library_dirs += tuple(lib_dirs)
    if options.platforms:
       app_platforms = options.platforms.split(',')
    print app_name, app_library_dirs
    build(app_name, options.output, options.js_includes, options.debug)

if __name__ == "__main__":
    main()

<|endoftext|>


<commit_before># The MIT License (MIT)
#
# Copyright (c) 2017 Dan Halbert
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#

"""
`adafruit_hid.mouse.Mouse`
====================================================

* Author(s): Dan Halbert
"""
import time
import usb_hid

class Mouse:
    """Send USB HID mouse reports."""

    LEFT_BUTTON = 1
    """Left mouse button."""
    RIGHT_BUTTON = 2
    """Right mouse button."""
    MIDDLE_BUTTON = 4
    """Middle mouse button."""

    def __init__(self):
        """Create a Mouse object that will send USB mouse HID reports."""
        self.hid_mouse = None
        for device in usb_hid.devices:
            if device.usage_page == 0x1 and device.usage == 0x02:
                self.hid_mouse = device
                break
        if not self.hid_mouse:
            raise IOError("Could not find an HID mouse device.")

        # Reuse this bytearray to send mouse reports.
        # report[0] buttons pressed (LEFT, MIDDLE, RIGHT)
        # report[1] x movement
        # report[2] y movement
        # report[3] wheel movement
        self.report = bytearray(4)

        # Do a no-op to test if HID device is ready.
        # If not, wait a bit and try once more.
        try:
            self.move(0, 0, 0)
        except OSError:
            time.sleep(1)
            self.move(0, 0, 0)

    def press(self, buttons):
        """Press the given mouse buttons.

        :param buttons: a bitwise-or'd combination of ``LEFT_BUTTON``,
            ``MIDDLE_BUTTON``, and ``RIGHT_BUTTON``.

        Examples::

            # Press the left button.
            m.press(Mouse.LEFT_BUTTON)

            # Press the left and right buttons simultaneously.
            m.press(Mouse.LEFT_BUTTON | Mouse.RIGHT_BUTTON)
        """
        self.report[0] |= buttons
        self.move(0, 0, 0)

    def release(self, buttons):
        """Release the given mouse buttons.

        :param buttons: a bitwise-or'd combination of ``LEFT_BUTTON``,
            ``MIDDLE_BUTTON``, and ``RIGHT_BUTTON``.
        """
        self.report[0] &= ~buttons
        self.move(0, 0, 0)

    def release_all(self):
        """Release all the mouse buttons."""
        self.report[0] = 0
        self.move(0, 0, 0)

    def click(self, buttons):
        """Press and release the given mouse buttons.

        :param buttons: a bitwise-or'd combination of ``LEFT_BUTTON``,
            ``MIDDLE_BUTTON``, and ``RIGHT_BUTTON``.

        Examples::

            # Click the left button.
            m.click(Mouse.LEFT_BUTTON)

            # Double-click the left button.
            m.click(Mouse.LEFT_BUTTON)
            m.click(Mouse.LEFT_BUTTON)
        """

        self.press(buttons)
        self.release(buttons)

    def move(self, x=0, y=0, wheel=0):
        """Move the mouse and turn the wheel as directed.

        :param x: Move the mouse along the x axis. Negative is to the left, positive
            is to the right.
        :param y: Move the mouse along the y axis. Negative is upwards on the display,
            positive is downwards.
        :param wheel: Rotate the wheel this amount. Negative is toward the user, positive
            is away from the user. The scrolling effect depends on the host.

        Examples::

            # Move 100 to the left. Do not move up and down. Do not roll the scroll wheel.
            m.move(-100, 0, 0)
            # Same, with keyword arguments.
            m.move(x=-100)

            # Move diagonally to the upper right.
            m.move(50, 20)
            # Same.
            m.move(x=50, y=-20)

            # Roll the mouse wheel away from the user.
            m.move(wheel=1)
        """

        # Send multiple reports if necessary to move or scroll requested amounts.
        while x != 0 or y != 0 or wheel != 0:
            partial_x = self._limit(x)
            partial_y = self._limit(y)
            partial_wheel = self._limit(wheel)
            self.report[1] = partial_x
            self.report[2] = partial_y
            self.report[3] = partial_wheel
            self.hid_mouse.send_report(self.report)
            x -= partial_x
            y -= partial_y
            wheel -= partial_wheel

    @staticmethod
    def _limit(dist):
        return min(127, max(-127, dist))
<commit_msg>Fix Mouse bug introduced in 3.0.0<commit_after># The MIT License (MIT)
#
# Copyright (c) 2017 Dan Halbert
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#

"""
`adafruit_hid.mouse.Mouse`
====================================================

* Author(s): Dan Halbert
"""
import time
import usb_hid

class Mouse:
    """Send USB HID mouse reports."""

    LEFT_BUTTON = 1
    """Left mouse button."""
    RIGHT_BUTTON = 2
    """Right mouse button."""
    MIDDLE_BUTTON = 4
    """Middle mouse button."""

    def __init__(self):
        """Create a Mouse object that will send USB mouse HID reports."""
        self.hid_mouse = None
        for device in usb_hid.devices:
            if device.usage_page == 0x1 and device.usage == 0x02:
                self.hid_mouse = device
                break
        if not self.hid_mouse:
            raise IOError("Could not find an HID mouse device.")

        # Reuse this bytearray to send mouse reports.
        # report[0] buttons pressed (LEFT, MIDDLE, RIGHT)
        # report[1] x movement
        # report[2] y movement
        # report[3] wheel movement
        self.report = bytearray(4)

        # Do a no-op to test if HID device is ready.
        # If not, wait a bit and try once more.
        try:
            self._send_no_move()
        except OSError:
            time.sleep(1)
            self._send_no_move()

    def press(self, buttons):
        """Press the given mouse buttons.

        :param buttons: a bitwise-or'd combination of ``LEFT_BUTTON``,
            ``MIDDLE_BUTTON``, and ``RIGHT_BUTTON``.

        Examples::

            # Press the left button.
            m.press(Mouse.LEFT_BUTTON)

            # Press the left and right buttons simultaneously.
            m.press(Mouse.LEFT_BUTTON | Mouse.RIGHT_BUTTON)
        """
        self.report[0] |= buttons
        self._send_no_move()

    def release(self, buttons):
        """Release the given mouse buttons.

        :param buttons: a bitwise-or'd combination of ``LEFT_BUTTON``,
            ``MIDDLE_BUTTON``, and ``RIGHT_BUTTON``.
        """
        self.report[0] &= ~buttons
        self._send_no_move()

    def release_all(self):
        """Release all the mouse buttons."""
        self.report[0] = 0
        self._send_no_move()

    def click(self, buttons):
        """Press and release the given mouse buttons.

        :param buttons: a bitwise-or'd combination of ``LEFT_BUTTON``,
            ``MIDDLE_BUTTON``, and ``RIGHT_BUTTON``.

        Examples::

            # Click the left button.
            m.click(Mouse.LEFT_BUTTON)

            # Double-click the left button.
            m.click(Mouse.LEFT_BUTTON)
            m.click(Mouse.LEFT_BUTTON)
        """
        self.press(buttons)
        self.release(buttons)

    def move(self, x=0, y=0, wheel=0):
        """Move the mouse and turn the wheel as directed.

        :param x: Move the mouse along the x axis. Negative is to the left, positive
            is to the right.
        :param y: Move the mouse along the y axis. Negative is upwards on the display,
            positive is downwards.
        :param wheel: Rotate the wheel this amount. Negative is toward the user, positive
            is away from the user. The scrolling effect depends on the host.

        Examples::

            # Move 100 to the left. Do not move up and down. Do not roll the scroll wheel.
            m.move(-100, 0, 0)
            # Same, with keyword arguments.
            m.move(x=-100)

            # Move diagonally to the upper right.
            m.move(50, 20)
            # Same.
            m.move(x=50, y=-20)

            # Roll the mouse wheel away from the user.
            m.move(wheel=1)
        """
        # Send multiple reports if necessary to move or scroll requested amounts.
        while x != 0 or y != 0 or wheel != 0:
            partial_x = self._limit(x)
            partial_y = self._limit(y)
            partial_wheel = self._limit(wheel)
            self.report[1] = partial_x
            self.report[2] = partial_y
            self.report[3] = partial_wheel
            self.hid_mouse.send_report(self.report)
            x -= partial_x
            y -= partial_y
            wheel -= partial_wheel

    def _send_no_move(self):
        """Send a button-only report."""
        self.report[1] = 0
        self.report[2] = 0
        self.report[3] = 0
        self.hid_mouse.send_report(self.report)

    @staticmethod
    def _limit(dist):
        return min(127, max(-127, dist))
<|endoftext|>


<commit_before># coding: utf-8
from typing import Callable, Dict, Optional, List, Set, Any, Tuple
from collections import deque, defaultdict

from siebenapp.domain import (
    Graph,
    EdgeType,
    Edge,
    HoldSelect,
    ToggleClose,
    Delete,
    ToggleLink,
    Add,
    Select,
    Insert,
    Rename,
)

GoalsData = List[Tuple[int, Optional[str], bool]]
EdgesData = List[Tuple[int, int, EdgeType]]
OptionsData = List[Tuple[str, int]]

class Goals(Graph):
    ROOT_ID = 1

    def __init__(self, name: str, message_fn: Callable[[str], None] = None) -> None:
        super().__init__()
        self.goals: Dict[int, Optional[str]] = {}
        self.edges: Dict[Tuple[int, int], EdgeType] = {}
        self.edges_forward: Dict[int, Dict[int, EdgeType]] = defaultdict(
            lambda: defaultdict(lambda: EdgeType.BLOCKER)
        )
        self.closed: Set[int] = set()
        self.selection = Goals.ROOT_ID
        self.previous_selection = Goals.ROOT_ID
        self._events: deque = deque()
        self.message_fn = message_fn
        self._add_no_link(name)

    def _msg(self, message: str) -> None:
        if self.message_fn:
            self.message_fn(message)

    def _has_link(self, lower: int, upper: int) -> bool:
        return (lower, upper) in self.edges

    def _forward_edges(self, goal: int) -> List[Edge]:
        return [Edge(goal, k, v) for k, v in self.edges_forward[goal].items()]

    def _back_edges(self, goal: int) -> List[Edge]:
        return [Edge(k[0], goal, v) for k, v in self.edges.items() if k[1] == goal]

    def _parent(self, goal: int) -> int:
        parents = {e for e in self._back_edges(goal) if e.type == EdgeType.PARENT}
        return parents.pop().source if parents else Goals.ROOT_ID

    def settings(self, key: str) -> int:
        return {
            "selection": self.selection,
            "previous_selection": self.previous_selection,
        }.get(key, 0)

    def events(self) -> deque:
        return self._events

    def accept_Add(self, command: Add) -> bool:
        add_to = command.add_to or self.selection
        if add_to in self.closed:
            self._msg("A new subgoal cannot be added to the closed one")
            return False
        next_id = self._add_no_link(command.name)
        self.accept_ToggleLink(ToggleLink(add_to, next_id, command.edge_type))
        return True

    def _add_no_link(self, name: str) -> int:
        next_id = max(list(self.goals.keys()) + [0]) + 1
        self.goals[next_id] = name
        self._events.append(("add", next_id, name, True))
        return next_id

    def accept_Select(self, command: Select):
        goal_id = command.goal_id
        if goal_id in self.goals and self.goals[goal_id] is not None:
            self.selection = goal_id
            self._events.append(("select", goal_id))

    def accept_HoldSelect(self, command: HoldSelect):
        self.previous_selection = self.selection
        self._events.append(("hold_select", self.selection))

    def q(self, keys: str = "name") -> Dict[int, Any]:
        def sel(x: int) -> Optional[str]:
            if x == self.selection:
                return "select"
            if x == self.previous_selection:
                return "prev"
            return None

        keys_list = keys.split(",")
        result: Dict[int, Any] = dict()
        for key, name in ((k, n) for k, n in self.goals.items() if n is not None):
            value = {
                "edge": sorted([(e.target, e.type) for e in self._forward_edges(key)]),
                "name": name,
                "open": key not in self.closed,
                "select": sel(key),
                "switchable": self._switchable(key),
            }
            result[key] = {k: v for k, v in value.items() if k in keys_list}
        return result

    def _switchable(self, key: int) -> bool:
        if key in self.closed:
            has_parents = False
            for k in self.edges.keys():
                if k[1] == key:
                    has_parents = True
                    if k[0] not in self.closed:
                        return True
            return not has_parents
        return all(x.target in self.closed for x in self._forward_edges(key))

    def accept_Insert(self, command: Insert):
        if (lower := self.previous_selection) == (upper := self.selection):
            self._msg("A new goal can be inserted only between two different goals")
            return
        edge_type = self.edges.get((lower, upper), EdgeType.BLOCKER)
        if self.accept_Add(Add(command.name, lower, edge_type)):
            key = len(self.goals)
            self.accept_ToggleLink(ToggleLink(key, upper, edge_type))
            if self._has_link(lower, upper):
                self.accept_ToggleLink(ToggleLink(lower, upper))

    def accept_Rename(self, command: Rename):
        goal_id = command.goal_id or self.selection
        self.goals[goal_id] = command.new_name
        self._events.append(("rename", command.new_name, goal_id))

    def accept_ToggleClose(self, command: ToggleClose) -> None:
        if self.selection in self.closed:
            if self._may_be_reopened():
                self.closed.remove(self.selection)
                self._events.append(("toggle_close", True, self.selection))
            else:
                self._msg("This goal can't be reopened because other subgoals block it")
        else:
            if self._may_be_closed():
                self.closed.add(self.selection)
                self._events.append(("toggle_close", False, self.selection))
                self.accept_Select(
                    Select(self._first_open_and_switchable(command.root))
                )
                self.accept(HoldSelect())
            else:
                self._msg("This goal can't be closed because it have open subgoals")

    def _may_be_closed(self) -> bool:
        return all(g.target in self.closed for g in self._forward_edges(self.selection))

    def _may_be_reopened(self) -> bool:
        return all(
            k[0] not in self.closed
            for k, v in self.edges.items()
            if k[1] == self.selection
        )

    def _first_open_and_switchable(self, root: int) -> int:
        root = max(root, Goals.ROOT_ID)
        front = [root]
        candidates: List[int] = []
        while front:
            next_goal = front.pop()
            subgoals = [
                e.target
                for e in self._forward_edges(next_goal)
                if e.target not in self.closed and e.type == EdgeType.PARENT
            ]
            front.extend(subgoals)
            candidates.extend(g for g in subgoals if self._switchable(g))
        return min(candidates) if candidates else root

    def accept_Delete(self, command: Delete) -> None:
        if (goal_id := command.goal_id or self.selection) == Goals.ROOT_ID:
            self._msg("Root goal can't be deleted")
            return
        parent = self._parent(goal_id)
        self._delete_subtree(goal_id)
        self.accept_Select(Select(parent))
        self.accept(HoldSelect())

    def _delete_subtree(self, goal_id: int) -> None:
        parent = self._parent(goal_id)
        self.goals[goal_id] = None
        self.closed.add(goal_id)
        forward_edges = self._forward_edges(goal_id)
        next_to_remove = {e for e in forward_edges if e.type == EdgeType.PARENT}
        blockers = {e for e in forward_edges if e.type == EdgeType.BLOCKER}
        for back_edge in self._back_edges(goal_id):
            self.edges_forward[back_edge.source].pop(goal_id)
        self.edges_forward[goal_id].clear()
        self.edges = {k: v for k, v in self.edges.items() if goal_id not in k}
        for old_blocker in blockers:
            if not self._back_edges(old_blocker.target):
                self._create_new_link(parent, old_blocker.target, EdgeType.BLOCKER)
        for next_goal in next_to_remove:
            if not self._back_edges(next_goal.target):
                self._delete_subtree(next_goal.target)
        self._events.append(("delete", goal_id))

    def accept_ToggleLink(self, command: ToggleLink):
        if (lower := command.lower or self.previous_selection) == (
            upper := command.upper or self.selection
        ):
            self._msg("Goal can't be linked to itself")
            return
        if self._has_link(lower, upper):
            if (current_edge_type := self.edges[(lower, upper)]) != command.edge_type:
                self._replace_link(lower, upper, command.edge_type)
                self._transform_old_parents_into_blocked(lower, upper)
            else:
                self._remove_existing_link(lower, upper, current_edge_type)
        else:
            self._create_new_link(lower, upper, command.edge_type)

    def _replace_link(self, lower: int, upper: int, edge_type: EdgeType) -> None:
        old_edge_type = self.edges[(lower, upper)]
        self.edges[(lower, upper)] = edge_type
        self.edges_forward[lower][upper] = edge_type
        self._events.append(("link", lower, upper, edge_type))
        self._events.append(("unlink", lower, upper, old_edge_type))

    def _remove_existing_link(
        self, lower: int, upper: int, edge_type: int = None
    ) -> None:
        if len(self._back_edges(upper)) > 1:
            self.edges.pop((lower, upper))
            self.edges_forward[lower].pop(upper)
            self._events.append(("unlink", lower, upper, edge_type))
        else:
            self._msg("Can't remove the last link")

    def _create_new_link(self, lower: int, upper: int, edge_type: EdgeType) -> None:
        if lower in self.closed and upper not in self.closed:
            self._msg("An open goal can't block already closed one")
            return
        if self._has_circular_dependency(lower, upper):
            self._msg("Circular dependencies between goals are not allowed")
            return
        if edge_type == EdgeType.PARENT:
            self._transform_old_parents_into_blocked(lower, upper)
        self.edges[lower, upper] = edge_type
        self.edges_forward[lower][upper] = edge_type
        self._events.append(("link", lower, upper, edge_type))

    def _transform_old_parents_into_blocked(self, lower, upper):
        old_parents = [
            e.source
            for e in self._back_edges(upper)
            if e.type == EdgeType.PARENT and e.source != lower
        ]
        for p in old_parents:
            self._replace_link(p, upper, EdgeType.BLOCKER)

    def _has_circular_dependency(self, lower: int, upper: int) -> bool:
        front: Set[int] = {upper}
        visited: Set[int] = set()
        total: Set[int] = set()
        while front:
            g = front.pop()
            visited.add(g)
            for e in self._forward_edges(g):
                total.add(e.target)
                if e not in visited:
                    front.add(e.target)
        return lower in total

    def verify(self) -> bool:
        assert all(
            g.target in self.closed for p in self.closed for g in self._forward_edges(p)
        ), "Open goals could not be blocked by closed ones"

        queue: List[int] = [Goals.ROOT_ID]
        visited: Set[int] = set()
        while queue:
            goal = queue.pop()
            queue.extend(
                g.target
                for g in self._forward_edges(goal)
                if g.target not in visited and self.goals[g.target] is not None
            )
            visited.add(goal)
        assert visited == {
            x for x in self.goals if self.goals[x] is not None
        }, "All subgoals must be accessible from the root goal"

        deleted_nodes = [g for g, v in self.goals.items() if v is None]
        assert all(
            not self._forward_edges(n) for n in deleted_nodes
        ), "Deleted goals must have no dependencies"

        parent_edges = [k for k, v in self.edges.items() if v == EdgeType.PARENT]
        edges_with_parent = {child for parent, child in parent_edges}
        assert len(parent_edges) == len(
            edges_with_parent
        ), "Each goal must have at most 1 parent"

        return True

    @staticmethod
    def build(goals, edges, settings, message_fn=None):
        # type: (GoalsData, EdgesData, OptionsData, Callable[[str], None]) -> Goals
        result = Goals("", message_fn)
        result._events.clear()
        goals_dict = {g[0]: g[1] for g in goals}
        result.goals = {
            i: goals_dict.get(i) for i in range(1, max(goals_dict.keys()) + 1)
        }

        result.closed = {g[0] for g in goals if not g[2]}.union(
            {k for k, v in result.goals.items() if v is None}
        )

        for parent, child, link_type in edges:
            result.edges[parent, child] = EdgeType(link_type)
            result.edges_forward[parent][child] = EdgeType(link_type)
        selection_dict = dict(settings)
        result.selection = selection_dict.get("selection", result.selection)
        result.previous_selection = selection_dict.get(
            "previous_selection", result.previous_selection
        )
        result.verify()
        return result

    @staticmethod
    def export(goals):
        # type: (Goals) -> Tuple[GoalsData, EdgesData, OptionsData]
        nodes = [
            (g_id, g_name, g_id not in goals.closed)
            for g_id, g_name in goals.goals.items()
        ]

        edges = [(k[0], k[1], v) for k, v in goals.edges.items()]
        settings = [
            ("selection", goals.selection),
            ("previous_selection", goals.previous_selection),
        ]
        return nodes, edges, settings
<commit_msg>Use _back_edges() instead of manual loop<commit_after># coding: utf-8
from typing import Callable, Dict, Optional, List, Set, Any, Tuple
from collections import deque, defaultdict

from siebenapp.domain import (
    Graph,
    EdgeType,
    Edge,
    HoldSelect,
    ToggleClose,
    Delete,
    ToggleLink,
    Add,
    Select,
    Insert,
    Rename,
)

GoalsData = List[Tuple[int, Optional[str], bool]]
EdgesData = List[Tuple[int, int, EdgeType]]
OptionsData = List[Tuple[str, int]]

class Goals(Graph):
    ROOT_ID = 1

    def __init__(self, name: str, message_fn: Callable[[str], None] = None) -> None:
        super().__init__()
        self.goals: Dict[int, Optional[str]] = {}
        self.edges: Dict[Tuple[int, int], EdgeType] = {}
        self.edges_forward: Dict[int, Dict[int, EdgeType]] = defaultdict(
            lambda: defaultdict(lambda: EdgeType.BLOCKER)
        )
        self.closed: Set[int] = set()
        self.selection = Goals.ROOT_ID
        self.previous_selection = Goals.ROOT_ID
        self._events: deque = deque()
        self.message_fn = message_fn
        self._add_no_link(name)

    def _msg(self, message: str) -> None:
        if self.message_fn:
            self.message_fn(message)

    def _has_link(self, lower: int, upper: int) -> bool:
        return (lower, upper) in self.edges

    def _forward_edges(self, goal: int) -> List[Edge]:
        return [Edge(goal, k, v) for k, v in self.edges_forward[goal].items()]

    def _back_edges(self, goal: int) -> List[Edge]:
        return [Edge(k[0], goal, v) for k, v in self.edges.items() if k[1] == goal]

    def _parent(self, goal: int) -> int:
        parents = {e for e in self._back_edges(goal) if e.type == EdgeType.PARENT}
        return parents.pop().source if parents else Goals.ROOT_ID

    def settings(self, key: str) -> int:
        return {
            "selection": self.selection,
            "previous_selection": self.previous_selection,
        }.get(key, 0)

    def events(self) -> deque:
        return self._events

    def accept_Add(self, command: Add) -> bool:
        add_to = command.add_to or self.selection
        if add_to in self.closed:
            self._msg("A new subgoal cannot be added to the closed one")
            return False
        next_id = self._add_no_link(command.name)
        self.accept_ToggleLink(ToggleLink(add_to, next_id, command.edge_type))
        return True

    def _add_no_link(self, name: str) -> int:
        next_id = max(list(self.goals.keys()) + [0]) + 1
        self.goals[next_id] = name
        self._events.append(("add", next_id, name, True))
        return next_id

    def accept_Select(self, command: Select):
        goal_id = command.goal_id
        if goal_id in self.goals and self.goals[goal_id] is not None:
            self.selection = goal_id
            self._events.append(("select", goal_id))

    def accept_HoldSelect(self, command: HoldSelect):
        self.previous_selection = self.selection
        self._events.append(("hold_select", self.selection))

    def q(self, keys: str = "name") -> Dict[int, Any]:
        def sel(x: int) -> Optional[str]:
            if x == self.selection:
                return "select"
            if x == self.previous_selection:
                return "prev"
            return None

        keys_list = keys.split(",")
        result: Dict[int, Any] = dict()
        for key, name in ((k, n) for k, n in self.goals.items() if n is not None):
            value = {
                "edge": sorted([(e.target, e.type) for e in self._forward_edges(key)]),
                "name": name,
                "open": key not in self.closed,
                "select": sel(key),
                "switchable": self._switchable(key),
            }
            result[key] = {k: v for k, v in value.items() if k in keys_list}
        return result

    def _switchable(self, key: int) -> bool:
        if key in self.closed:
            has_parents = False
            back_edges = self._back_edges(key)
            if back_edges:
                has_parents = True
                if any(e.source not in self.closed for e in back_edges):
                    return True
            return not has_parents
        return all(x.target in self.closed for x in self._forward_edges(key))

    def accept_Insert(self, command: Insert):
        if (lower := self.previous_selection) == (upper := self.selection):
            self._msg("A new goal can be inserted only between two different goals")
            return
        edge_type = self.edges.get((lower, upper), EdgeType.BLOCKER)
        if self.accept_Add(Add(command.name, lower, edge_type)):
            key = len(self.goals)
            self.accept_ToggleLink(ToggleLink(key, upper, edge_type))
            if self._has_link(lower, upper):
                self.accept_ToggleLink(ToggleLink(lower, upper))

    def accept_Rename(self, command: Rename):
        goal_id = command.goal_id or self.selection
        self.goals[goal_id] = command.new_name
        self._events.append(("rename", command.new_name, goal_id))

    def accept_ToggleClose(self, command: ToggleClose) -> None:
        if self.selection in self.closed:
            if self._may_be_reopened():
                self.closed.remove(self.selection)
                self._events.append(("toggle_close", True, self.selection))
            else:
                self._msg("This goal can't be reopened because other subgoals block it")
        else:
            if self._may_be_closed():
                self.closed.add(self.selection)
                self._events.append(("toggle_close", False, self.selection))
                self.accept_Select(
                    Select(self._first_open_and_switchable(command.root))
                )
                self.accept(HoldSelect())
            else:
                self._msg("This goal can't be closed because it have open subgoals")

    def _may_be_closed(self) -> bool:
        return all(g.target in self.closed for g in self._forward_edges(self.selection))

    def _may_be_reopened(self) -> bool:
        return all(
            k[0] not in self.closed
            for k, v in self.edges.items()
            if k[1] == self.selection
        )

    def _first_open_and_switchable(self, root: int) -> int:
        root = max(root, Goals.ROOT_ID)
        front = [root]
        candidates: List[int] = []
        while front:
            next_goal = front.pop()
            subgoals = [
                e.target
                for e in self._forward_edges(next_goal)
                if e.target not in self.closed and e.type == EdgeType.PARENT
            ]
            front.extend(subgoals)
            candidates.extend(g for g in subgoals if self._switchable(g))
        return min(candidates) if candidates else root

    def accept_Delete(self, command: Delete) -> None:
        if (goal_id := command.goal_id or self.selection) == Goals.ROOT_ID:
            self._msg("Root goal can't be deleted")
            return
        parent = self._parent(goal_id)
        self._delete_subtree(goal_id)
        self.accept_Select(Select(parent))
        self.accept(HoldSelect())

    def _delete_subtree(self, goal_id: int) -> None:
        parent = self._parent(goal_id)
        self.goals[goal_id] = None
        self.closed.add(goal_id)
        forward_edges = self._forward_edges(goal_id)
        next_to_remove = {e for e in forward_edges if e.type == EdgeType.PARENT}
        blockers = {e for e in forward_edges if e.type == EdgeType.BLOCKER}
        for back_edge in self._back_edges(goal_id):
            self.edges_forward[back_edge.source].pop(goal_id)
        self.edges_forward[goal_id].clear()
        self.edges = {k: v for k, v in self.edges.items() if goal_id not in k}
        for old_blocker in blockers:
            if not self._back_edges(old_blocker.target):
                self._create_new_link(parent, old_blocker.target, EdgeType.BLOCKER)
        for next_goal in next_to_remove:
            if not self._back_edges(next_goal.target):
                self._delete_subtree(next_goal.target)
        self._events.append(("delete", goal_id))

    def accept_ToggleLink(self, command: ToggleLink):
        if (lower := command.lower or self.previous_selection) == (
            upper := command.upper or self.selection
        ):
            self._msg("Goal can't be linked to itself")
            return
        if self._has_link(lower, upper):
            if (current_edge_type := self.edges[(lower, upper)]) != command.edge_type:
                self._replace_link(lower, upper, command.edge_type)
                self._transform_old_parents_into_blocked(lower, upper)
            else:
                self._remove_existing_link(lower, upper, current_edge_type)
        else:
            self._create_new_link(lower, upper, command.edge_type)

    def _replace_link(self, lower: int, upper: int, edge_type: EdgeType) -> None:
        old_edge_type = self.edges[(lower, upper)]
        self.edges[(lower, upper)] = edge_type
        self.edges_forward[lower][upper] = edge_type
        self._events.append(("link", lower, upper, edge_type))
        self._events.append(("unlink", lower, upper, old_edge_type))

    def _remove_existing_link(
        self, lower: int, upper: int, edge_type: int = None
    ) -> None:
        if len(self._back_edges(upper)) > 1:
            self.edges.pop((lower, upper))
            self.edges_forward[lower].pop(upper)
            self._events.append(("unlink", lower, upper, edge_type))
        else:
            self._msg("Can't remove the last link")

    def _create_new_link(self, lower: int, upper: int, edge_type: EdgeType) -> None:
        if lower in self.closed and upper not in self.closed:
            self._msg("An open goal can't block already closed one")
            return
        if self._has_circular_dependency(lower, upper):
            self._msg("Circular dependencies between goals are not allowed")
            return
        if edge_type == EdgeType.PARENT:
            self._transform_old_parents_into_blocked(lower, upper)
        self.edges[lower, upper] = edge_type
        self.edges_forward[lower][upper] = edge_type
        self._events.append(("link", lower, upper, edge_type))

    def _transform_old_parents_into_blocked(self, lower, upper):
        old_parents = [
            e.source
            for e in self._back_edges(upper)
            if e.type == EdgeType.PARENT and e.source != lower
        ]
        for p in old_parents:
            self._replace_link(p, upper, EdgeType.BLOCKER)

    def _has_circular_dependency(self, lower: int, upper: int) -> bool:
        front: Set[int] = {upper}
        visited: Set[int] = set()
        total: Set[int] = set()
        while front:
            g = front.pop()
            visited.add(g)
            for e in self._forward_edges(g):
                total.add(e.target)
                if e not in visited:
                    front.add(e.target)
        return lower in total

    def verify(self) -> bool:
        assert all(
            g.target in self.closed for p in self.closed for g in self._forward_edges(p)
        ), "Open goals could not be blocked by closed ones"

        queue: List[int] = [Goals.ROOT_ID]
        visited: Set[int] = set()
        while queue:
            goal = queue.pop()
            queue.extend(
                g.target
                for g in self._forward_edges(goal)
                if g.target not in visited and self.goals[g.target] is not None
            )
            visited.add(goal)
        assert visited == {
            x for x in self.goals if self.goals[x] is not None
        }, "All subgoals must be accessible from the root goal"

        deleted_nodes = [g for g, v in self.goals.items() if v is None]
        assert all(
            not self._forward_edges(n) for n in deleted_nodes
        ), "Deleted goals must have no dependencies"

        parent_edges = [k for k, v in self.edges.items() if v == EdgeType.PARENT]
        edges_with_parent = {child for parent, child in parent_edges}
        assert len(parent_edges) == len(
            edges_with_parent
        ), "Each goal must have at most 1 parent"

        return True

    @staticmethod
    def build(goals, edges, settings, message_fn=None):
        # type: (GoalsData, EdgesData, OptionsData, Callable[[str], None]) -> Goals
        result = Goals("", message_fn)
        result._events.clear()
        goals_dict = {g[0]: g[1] for g in goals}
        result.goals = {
            i: goals_dict.get(i) for i in range(1, max(goals_dict.keys()) + 1)
        }

        result.closed = {g[0] for g in goals if not g[2]}.union(
            {k for k, v in result.goals.items() if v is None}
        )

        for parent, child, link_type in edges:
            result.edges[parent, child] = EdgeType(link_type)
            result.edges_forward[parent][child] = EdgeType(link_type)
        selection_dict = dict(settings)
        result.selection = selection_dict.get("selection", result.selection)
        result.previous_selection = selection_dict.get(
            "previous_selection", result.previous_selection
        )
        result.verify()
        return result

    @staticmethod
    def export(goals):
        # type: (Goals) -> Tuple[GoalsData, EdgesData, OptionsData]
        nodes = [
            (g_id, g_name, g_id not in goals.closed)
            for g_id, g_name in goals.goals.items()
        ]

        edges = [(k[0], k[1], v) for k, v in goals.edges.items()]
        settings = [
            ("selection", goals.selection),
            ("previous_selection", goals.previous_selection),
        ]
        return nodes, edges, settings
<|endoftext|>


<commit_before>#!/usr/bin/env python

import serial			    # pyserial library for serial communications
import struct 	     	# Python struct library for constructing the command data
from zlib import crc32      # used to calculate a message checksum
from time import time
# Definitions

FRAME_FLAG = 0x0C       # Marks the beginning and end of a frame
FRAME_ESC = 0x1B        # Escapes chars in a frame that have a special meaning

CMD_START = 0x00
CMD_START_ACK = 0x00
CMD_START_NACK = 0x00
CMD_STOP = 0x00
CMD_STOP_ACK = 0x00
CMD_STOP_NACK = 0x00
CMD_RESTART = 0x00
CMD_RESTART_ACK = 0x00
CMD_RESTART_NACK = 0x00

# Motor module specific definitions
CMD_FORWARD = 0x00
CMD_BACK = 0x00
CMD_LEFT = 0x00
CMD_RIGHT = 0x00

class HardwareController():

    """ Serial interface into the Arduino microcontroller

    We use a protocol similar to the PPP protocol used in for
    example ADSL connections. For each message sent over the
    serial connection we will add a FRAME_FLAG to the beginning
    and end. If by change the message itself contains the FRAME_FLAG
    or other special characters we precede the byte with an
    escape flag (FRAME_ESC).

    Since data can be lost or mangled in transmission due to hardware
    errors or electrical interference we perform a CRC check on the
    raw message and append that after the message.

     Frame layout
    +------------+---------+-----+------------+
    | FRAME_FLAG | MESSAGE | CRC | FRAME_FLAG |
    +------------+---------+-----+------------+

    Our message consists of the following fields:

    messageID      (unsigned long, 4 bytes, numeric id of the message)
    destination    (unsigned short, 2 byte, arduino module to target)
    commandType    (unsigned short, 1 byte, Type of command to send)
    dataLen        (unsigned short, 1 byte, lenght of message data in Bytes)
    data           (string, dataLen byte(s), the data payload)
    checksum       (unsigned int, 4 bytes, CRC32)

    The following generic commands are available currently:
        START, START_ACK,
        START_NACK:             Starts the specified module.
                                replies ACK with messageID in data field
                                when started, replies NACK if there was
                                an error
        RESTART, RESTART_ACK,
        RESTART_NACK:           Restarts the specified module,
                                replies ACK with messageID ind data field
                                when restarted, replies NACK if there was
                                an error
        STOP, STOP_ACK,
        STOP_NACK:              Stops the specified module, replies
                                with STOP_ACK with messageID in data field
                                when stopped, replies NACK if there was an
                                error
        DATA                    Sends data related to the specified module,
                                an example is a Distance Sensor reporting
                                back its values when STARTed

    The following modules are available currently:
        Arduino                 Controls the Arduino itself
        Motor                   Controls the robots motors
        Servo                   Controls the Servo motors
        DistanceSensor          Handles the distance sensor
        AccelerationSensor      Handles the Acceleration sensor
        CompassSensor           Handles the Compass sensor
    """

    __lastMessageID = 0         # holds the last used messageID

    def initialize(self, serialPort='/dev/ttyACM0',
                   baudrate=9600,
                   stopbits=serial.STOPBITS_ONE,
                   bytesize=serial.EIGHTBITS,
                   timeout=0):
        """ The initialisation for the ArduinoSerialController class

        Args:
          serialPort (str): The port used to communicate with the Arduino
          baudrate (int): The baudrate of the serial connection
          stopbits (int): The stopbits of the serial connection
          bytesize (int): The bytesize of the serial connection
          timeout (float): The timeout for the serial read command
                           None wait forever
                           0 non blocking
                           x set timeout to x seconds (float allowed)
        """
        self.serialPort = serial.Serial(serialPort, baudrate)

        '''  Reset the arduino by setting the DTR pin LOW and then
        HIGH again. This is the same as pressing the reset button
        on the Arduino itself. The flushInput() whilst the reset
        is in progress is to ensure there is no data from before
        the Arduino was reset in the serial buffer '''

        self.serialPort.setDTR(level=False)
        time.sleep(0.5)
        self.serialPort.flushInput()
        self.serialPort.setDTR()
        time.sleep(0.5)

        ''' TODO implement a handshake between the arduino and Pi
        make sure we're not doing anything until the handshake is
        finalised. '''

        self.serialPort.timeout = 5     # Set blocking read to 5 sec
        handshake = self.serialPort.read()
        if handshake == "OK":
            print "Handshare succesfully"
        self.serialPort.timeout = 0     # set non-blocking read

    def __del__(self):
        """ Close the serial connection when the class is deleted """
        try:
            self.serialPort.close()
        except:
            pass

    def __packMessage(self, module, commandType, data):
        """ Creates a message understood by the Arduino

          Message structure
        +-----------+--------+-------------+---------+------+----------+
        | messageID | module | commandType | dataLen | data | checksum |
        +-----------+--------+-------------+---------+------+----------+

        Args:
            module:      (unsigned int, 1 byte, arduino module to target)
            commandType: (unsigned int, 1 byte, Type of command to send)
            data:        (string, dataLen byte(s), the data payload)

        Returns:
            Message byte string

        TODO:
            Need to do validation checks for all given arguments
            Implement CRC checksum on raspberry and arduino
        """

        self.__lastMessageID += 1

        # pack the message into binary format using ! for platform independency
        # (big-endian, standard type size, no pad bytes). Then calculate
        # the packet checksum and repack the message

        checksum = 0
        rawMessage = struct.pack('!LccHsi',
                                 self.__lastMessageID,
                                 module,
                                 commandType,
                                 len(data),
                                 data,
                                 checksum)

        checksum = crc32(rawMessage[-4])

        rawMessage = struct.pack('!LccHsi',
                                 self.__lastMessageID,
                                 module,
                                 commandType,
                                 len(data),
                                 data,
                                 checksum)

        return rawMessage

    def __unpackMessage(self, message):
        """ Unpacks a message received from the Arduino

        Args:
            message (struct): A message unpacked from a received FRAME_FLAG

        Returns:
            A dict containing:
               messageID
               module
               commandType
               dataLen
               data
        """

        (messageID, module, commandType,
         dataLen, data, recvChecksum) = struct.unpack('!LccHsi', message)

        checksum = 0
        calcChecksum = crc32(struct.pack('!LccHsi',
                                         messageID,
                                         module,
                                         commandType,
                                         dataLen,
                                         data,
                                         checksum)[-4])

        if recvChecksum == calcChecksum:
            return messageID, module, commandType, dataLen, data
        else:
            return None     # invalid packet

    def __packFrame(self, message):
        """ Packs the command into a frame

        Performs checksum calculation, escapes any potential
        characters that matches a frame control flag and applies
        the frame marker to the beginning and end of the frame

        Args:
            message (struct message): The message to be sent to
                                      the arduino

        Returns:
            A packed frame suitable for sending to the arduino
            over the serial connection. """

        frame = b''
        frame += chr(FRAME_FLAG)

        for byte in message:
            if (byte == chr(FRAME_ESC)) or (byte == chr(FRAME_FLAG)):
                frame += chr(FRAME_ESC)
                frame += byte          # TODO, Need to XOR this value?
            else:
                frame += byte

        frame += chr(FRAME_FLAG)

        return frame

    def __unpackFrame(self, frame):
        """ Unpacks a received frame from the arduino

        Args:
            frame (string): The received frame from
                            the arduino
        Returns:
            message (struct message): A received message from the
                                      arduino
        """

        nextByteValid = False
        message = b''

        if(frame[:1] != chr(FRAME_FLAG)) or (frame[-1:] != chr(FRAME_FLAG)):
            print "Invalid frame received, frame flag not valid"
        else:
            for byte in frame:
                if nextByteValid:
                    message += byte
                    nextByteValid = False
                elif (byte == chr(FRAME_ESC)):
                        nextByteValid = True
                elif (byte != chr(FRAME_FLAG)):
                        message += byte

        return message

    def sendMessage(self, module, commandType, data):
        """ Send data onto the serial port towards the arduino.

        Used by the HardwareController class to send commands.

        Args:
          data (str): The data string to send to the arduino. This
            is used by the public sendCommand() function
        """

        print "morTimmy: %s %s %s" % (module, commandType, data)
        self.serialPort.write(' '.join([module, commandType, data]))

    def recvMessage(self):
        """ Receive data from the Arduino through the serial port.

        Used by the HardwareController class to receive
        commands from the Arduino.

        Returns:
          returns a single frame received through the serial controller.
        """
        data = self.serialPort.readline()
        if data:
            return data.strip()
        else:
            return None

    def testMessagePacking(self, module, commandType, data):
        """ Test for the message pack and unpack functions """

        print "Packing message..."
        packedMessage = self.__packMessage(module, commandType, data)
        print ''.join(["\\x%02x" % ord(x) for x in packedMessage])

        print "Packing message into frame..."
        frame = self.__packFrame(packedMessage)
        print ''.join(["\\x%02x" % ord(x) for x in frame])

        print "Unpacking message from frame..."
        unpackedFrame = self.__unpackFrame(frame)
        print ''.join(["\\x%02x" % ord(x) for x in unpackedFrame])

        print "Unpacking message..."
        unpackedMessage = self.__unpackMessage(packedMessage)
        print unpackedMessage

def main():
    """ This function will only be called when the library is
    run directly. Only to be used to do quick tests on the library.
    """

    try:
        hwControl = HardwareController()
    except Exception as e:
        print ("Error, could not establish connection to "
               "Arduino through the serial port.\n%s") % e
#        exit()

    hwControl.testMessagePacking('a', 'b', 'c')

if __name__ == '__main__':
    main()
<commit_msg>Added a few command definitions and refined the sendCommand() function<commit_after>#!/usr/bin/env python

import serial			    # pyserial library for serial communications
import struct 	     	# Python struct library for constructing the command data
from zlib import crc32      # used to calculate a message checksum
from time import time

# Definitions

# Frames
FRAME_FLAG = 0x0C       # Marks the start and end of a frame
FRAME_ESC = 0x1B        # Escape char for frame

# Arduino
MODULE_ARDUINO = 0x030
CMD_ARDUINO_START = 0x64
CMD_ARDUINO_START_NACK = 0x65
CMD_ARDUINO_STOP = 0x66
CMD_ARDUINO_STOP_NACK = 0x67
CMD_ARDUINO_RESTART = 0x68
CMD_ARDUINO_RESTART_NACK = 0x69

# Distance Sensor
MODULE_DISTANCE_SENSOR = 0x31
CMD_DISTANCE_SENSOR_START = 0x64
CMD_DISTANCE_SENSOR_NACK = 0x65
CMD_DISTANCE_SENSOR_STOP = 0x66
CMD_DISTANCE_SENSOR_STOP_NACK = 0x67

# Motor
MODULE_MOTOR = 0x32
CMD_MOTOR_FORWARD = 0x64
CMD_MOTOR_FORWARD_NACK = 0x65
CMD_MOTOR_BACK = 0x66
CMD_MOTOR_FORWARD_NACK = 0x67
CMD_MOTOR_LEFT = 0x68
CMD_MOTOR_LEFT_NACK = 0x69
CMD_MOTOR_RIGHT = 0x6A
CMD_MOTOR_RIGHT_NACK = 0x6B
CMD_MOTOR_STOP = 0x6C
CMD_MOTOR_STOP_NACK = 0x6D

class HardwareController():

    """ Serial interface into the Arduino microcontroller

    We use a protocol similar to the PPP protocol used in for
    example ADSL connections. For each message sent over the
    serial connection we will add a FRAME_FLAG to the beginning
    and end. If by change the message itself contains the FRAME_FLAG
    or other special characters we precede the byte with an
    escape flag (FRAME_ESC).

    Since data can be lost or mangled in transmission due to hardware
    errors or electrical interference we perform a CRC check on the
    raw message and append that after the message.

     Frame layout
    +------------+---------+-----+------------+
    | FRAME_FLAG | MESSAGE | CRC | FRAME_FLAG |
    +------------+---------+-----+------------+

    Our message consists of the following fields:

    messageID      (unsigned long, 4 bytes, numeric id of the message)
    acknowledgeID  (unsigned long, 4 bytes, numeric id of the messageID
                    it replied to)
    module         (unsigned short, 2 byte, arduino module to target)
    commandType    (unsigned short, 1 byte, Type of command to send)
    dataLen        (unsigned short, 1 byte, lenght of message data in Bytes)
    data           (string, dataLen byte(s), the data payload)
    checksum       (unsigned int, 4 bytes, CRC32)

    The commandType depicts the action that has to take place on the
    specified module. If the action is succesful the other party will
    reply with the same module and commandType. In addition it will
    put the messageID of the original request in the acknowledgeID field.
    If the command failed to run a NACK messaage will be send with the
    acknowledgeID field filled in.

    The data field contains data that might be returned like the distance
    from a distance sensor.

    The following modules are available currently:
        Arduino                 Controls the Arduino itself
        Motor                   Controls the robots motors
        Servo                   Controls the Servo motors
        DistanceSensor          Handles the distance sensor
        AccelerationSensor      Handles the Acceleration sensor
        CompassSensor           Handles the Compass sensor
    """

    __lastMessageID = 0         # holds the last used messageID

    def initialize(self, serialPort='/dev/ttyACM0',
                   baudrate=9600,
                   stopbits=serial.STOPBITS_ONE,
                   bytesize=serial.EIGHTBITS,
                   timeout=0):
        """ The initialisation for the ArduinoSerialController class

        Args:
          serialPort (str): The port used to communicate with the Arduino
          baudrate (int): The baudrate of the serial connection
          stopbits (int): The stopbits of the serial connection
          bytesize (int): The bytesize of the serial connection
          timeout (float): The timeout for the serial read command
                           None wait forever
                           0 non blocking
                           x set timeout to x seconds (float allowed)
        """
        self.serialPort = serial.Serial(serialPort, baudrate)

        '''  Reset the arduino by setting the DTR pin LOW and then
        HIGH again. This is the same as pressing the reset button
        on the Arduino itself. The flushInput() whilst the reset
        is in progress is to ensure there is no data from before
        the Arduino was reset in the serial buffer '''

        self.serialPort.setDTR(level=False)
        time.sleep(0.5)
        self.serialPort.flushInput()
        self.serialPort.setDTR()
        time.sleep(0.5)

        ''' TODO implement a handshake between the arduino and Pi
        make sure we're not doing anything until the handshake is
        finalised. '''

        self.serialPort.timeout = 5     # Set blocking read to 5 sec
        handshake = self.serialPort.read()
        if handshake == "OK":
            print "Handshare succesfully"
        self.serialPort.timeout = 0     # set non-blocking read

    def __del__(self):
        """ Close the serial connection when the class is deleted """
        try:
            self.serialPort.close()
        except:
            pass

    def __packMessage(self, module, commandType, data='', acknowledgeID=0):
        """ Creates a message understood by the Arduino

          Message structure
        +-----------+--------+-------------+---------+------+----------+
        | messageID | module | commandType | dataLen | data | checksum |
        +-----------+--------+-------------+---------+------+----------+

        Args:
            module:      (unsigned int, 1 byte, arduino module to target)
            commandType: (unsigned int, 1 byte, Type of command to send)
            data:        (string, dataLen byte(s), the data payload)

        Returns:
            Message byte string

        TODO:
            Need to do validation checks for all given arguments
            Implement CRC checksum on raspberry and arduino
        """

        self.__lastMessageID += 1

        # pack the message into binary format using ! for platform independency
        # (big-endian, standard type size, no pad bytes). Then calculate
        # the packet checksum and repack the message

        checksum = 0
        rawMessage = struct.pack('!LLccHsi',
                                 self.__lastMessageID,
                                 acknowledgeID,
                                 chr(module),
                                 chr(commandType),
                                 len(data),
                                 data,
                                 checksum)

        checksum = crc32(rawMessage[-4])

        rawMessage = struct.pack('!LLccHsi',
                                 self.__lastMessageID,
                                 acknowledgeID,
                                 chr(module),
                                 chr(commandType),
                                 len(data),
                                 data,
                                 checksum)

        return rawMessage

    def __unpackMessage(self, message):
        """ Unpacks a message received from the Arduino

        Args:
            message (struct): A message unpacked from a received FRAME_FLAG

        Returns:
            A dict containing:
               messageID
               module
               commandType
               dataLen
               data
        """

        (messageID, acknowledgeID, module, commandType,
         dataLen, data, recvChecksum) = struct.unpack('!LLccHsi', message)

        checksum = 0
        calcChecksum = crc32(struct.pack('!LLccHsi',
                                         messageID,
                                         acknowledgeID,
                                         module,
                                         commandType,
                                         dataLen,
                                         data,
                                         checksum)[-4])

        if recvChecksum == calcChecksum:
            return messageID, acknowledgeID, ord(module), ord(commandType), dataLen, data
        else:
            return None     # invalid packet

    def __packFrame(self, message):
        """ Packs the command into a frame

        Performs checksum calculation, escapes any potential
        characters that matches a frame control flag and applies
        the frame marker to the beginning and end of the frame

        Args:
            message (struct message): The message to be sent to
                                      the arduino

        Returns:
            A packed frame suitable for sending to the arduino
            over the serial connection. """

        frame = b''
        frame += chr(FRAME_FLAG)

        for byte in message:
            if (byte == chr(FRAME_ESC)) or (byte == chr(FRAME_FLAG)):
                frame += chr(FRAME_ESC)
                frame += byte          # TODO, Need to XOR this value?
            else:
                frame += byte

        frame += chr(FRAME_FLAG)

        return frame

    def __unpackFrame(self, frame):
        """ Unpacks a received frame from the arduino

        Args:
            frame (string): The received frame from
                            the arduino
        Returns:
            message (struct message): A received message from the
                                      arduino
        """

        nextByteValid = False
        message = b''

        if(frame[:1] != chr(FRAME_FLAG)) or (frame[-1:] != chr(FRAME_FLAG)):
            print "Invalid frame received, frame flag not valid"
        else:
            for byte in frame:
                if nextByteValid:
                    message += byte
                    nextByteValid = False
                elif (byte == chr(FRAME_ESC)):
                        nextByteValid = True
                elif (byte != chr(FRAME_FLAG)):
                        message += byte

        return message

    def sendMessage(self, module, commandType, data, acknowledgeID=0):
        """ Send data onto the serial port towards the arduino.

        Used by the HardwareController class to send commands.

        Args:
          data (str): The data string to send to the arduino. This
            is used by the public sendCommand() function
        """

        packedMessage = self.__packMessage(module,
                                           commandType,
                                           data,
                                           acknowledgeID)
        packedFrame = self.__packFrame(packedMessage)

        print ("morTimmy: "
               "msgID=%d "
               "ackID=%d "
               "module=%s "
               "cmd=%s "
               "dataLen=%d "
               "data=%s ") % (self.__lastMessageID, acknowledgeID, hex(module),
                            hex(commandType), len(data), data)
        #self.serialPort.write(packedFrame)

    def recvMessage(self):
        """ Receive data from the Arduino through the serial port.

        Used by the HardwareController class to receive
        commands from the Arduino.

        Returns:
          returns a single frame received through the serial controller.
        """
        data = self.serialPort.readline()
        if data:
            return data.strip()
        else:
            return None

    def testMessagePacking(self, module, commandType, data):
        """ Test for the message pack and unpack functions """

        print "Packing message..."
        packedMessage = self.__packMessage(module, commandType, data)
        print ''.join(["\\x%02x" % ord(x) for x in packedMessage])

        print "Packing message into frame..."
        frame = self.__packFrame(packedMessage)
        print ''.join(["\\x%02x" % ord(x) for x in frame])

        print "Unpacking message from frame..."
        unpackedFrame = self.__unpackFrame(frame)
        print ''.join(["\\x%02x" % ord(x) for x in unpackedFrame])

        print "Unpacking message..."
        unpackedMessage = self.__unpackMessage(packedMessage)
        print unpackedMessage

def main():
    """ This function will only be called when the library is
    run directly. Only to be used to do quick tests on the library.
    """

    try:
        hwControl = HardwareController()
    except Exception as e:
        print ("Error, could not establish connection to "
               "Arduino through the serial port.\n%s") % e
#        exit()

    hwControl.sendMessage(MODULE_MOTOR, CMD_MOTOR_FORWARD, 'c')

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


<commit_before>from pushbullet import Pushbullet
import glob
import os

import json
import youtube_dl

import click

@click.group()
def cli():
    """A tool to download, convert and send youtube music and videos using Pushbullet"""
    pass

@cli.command()
@click.option('--link', prompt='Video/Playlist link to download',
              help='Paste the link here to download and send to your device')
@click.option('--newdevice', help='Overwrite the saved device')
@click.option('--video', is_flag=bool, default=False,
              help='Send the video file instead of mp3 file.')
@click.option('--delete', is_flag=bool, default=False,
              help='Delete the music file after sending.')
def download(link, newdevice, video, delete):
    """Download the youtube song or video and send it to a device"""
    if os.path.exists('data.txt'):
        with open('data.txt') as json_file:
            data = json.load(json_file)
            for p in data['user']:
                api_key = p['api_key']
                device = p['device']
    else:
        click.secho(
            "Please run `moboff initialise` first.",
            fg='red',
            bold=True)
        quit()

    os.chdir('Music')

    class MyLogger(object):
        def debug(self, msg):
            pass

        def warning(self, msg):
            pass

        def error(self, msg):
            print(msg)

    def my_hook(d):
        if d['status'] == 'finished':
            print('Done downloading, now converting ...')

    ydl_opts = {
        'forcefilename': 'True',
        'format': 'bestaudio/best',
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'mp3',
            'preferredquality': '192',
        }],
        'logger': MyLogger(),
        'progress_hooks': [my_hook],
    }

    if video is True:
        ydl_opts = {
            'logger': MyLogger(),
            'progress_hooks': [my_hook],
        }

    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        ydl.download([link])

    list_of_mp3_files = glob.glob('*mp3')
    list_of_mp4_files = glob.glob('*mp4')
    recent_download = max(
        list_of_mp3_files +
        list_of_mp4_files,
        key=os.path.getctime)

    print("File to send : {0}".format(recent_download))

    pb = Pushbullet(api_key)
    phone = device

    if(newdevice):
        phone = newdevice

    with open(recent_download, "rb") as song:
        file_data = pb.upload_file(song, recent_download)

    print("Now sending the file to {0}".format(phone))
    #push = pb.push_file(**file_data)
    if(delete):
        os.remove(recent_download)

@cli.command()
def initialise():
    """Initialise the program with yout API key and preffered device."""
    click.secho(
        "Please enter your API Key, you can obtain it from here: https://www.pushbullet.com/#settings/account",
        bold=True)
    api_key = input()

    click.secho(
        "Enter the serial number for your preffered device to send your music files",
        bold=True)

    pb = Pushbullet(api_key)

    i = 0
    for device in pb.devices:
        print("{0} : {1}".format(i + 1, device))
        i += 1

    device_id = int(input()) - 1

    if not os.path.exists('Music'):
        os.makedirs('Music')
    click.secho(
        "The music would be downloaded to {0}/Music".format(os.getcwd()))

    data = {}
    data['user'] = []
    data['user'].append({
        'api_key': api_key,
        'device': str(pb.devices[device_id]),
    })
    with open('data.txt', 'w') as outfile:
        json.dump(data, outfile)

    click.secho("Now you can run `moboff download` :) ", fg="green", bold=True)

if __name__ == '__main__':
    cli()<commit_msg>Changed to subprocess<commit_after>from pushbullet import Pushbullet
import glob
import os
import subprocess
import json

import click

@click.group()
def cli():
    """A tool to download, convert and send youtube music and videos using Pushbullet"""
    pass

@cli.command()
@click.option('--link', prompt='Video/Playlist link to download',
              help='Paste the link here to download and send to your device')
@click.option('--newdevice', help='Overwrite the saved device')
@click.option('--video', is_flag=bool, default=False,
              help='Send the video file instead of mp3 file.')
@click.option('--delete', is_flag=bool, default=False,
              help='Delete the music file after sending.')
def download(link, newdevice, video, delete):
    """Download the youtube song or video and send it to a device"""
    if os.path.exists('data.txt'):
        with open('data.txt') as json_file:
            data = json.load(json_file)
            for p in data['user']:
                api_key = p['api_key']
                device = p['device']
    else:
        click.secho(
            "Please run `moboff initialise` first.",
            fg='red',
            bold=True)
        quit()

    os.chdir('Music')

    if video is True:
        subprocess.call(["youtube-dl",
                         "--metadata-from-title",
                         "%(artist)s - %(title)s",
                         "-f",
                         "bestvideo+bestaudio",
                         "--add-metadata",
                         "--output",
                         "%(artist)s - %(title)s.%(ext)s",
                         link])
    else:
        subprocess.call(["youtube-dl",
                         "--metadata-from-title",
                         "%(artist)s - %(title)s",
                         "--extract-audio",
                         "--audio-format",
                         "mp3",
                         "--audio-quality",
                         "0",
                         "--add-metadata",
                         "--output",
                         "%(artist)s - %(title)s.%(ext)s",
                         link])

    types = ('*.mp3', '*.mp4', '*.mkv')
    list_of_files = []
    for files in types:
        list_of_files.extend(glob.glob(files))

    recent_download = max(
        list_of_files,
        key=os.path.getctime)

    print("File to send : {0}".format(recent_download))

    pb = Pushbullet(api_key)
    phone = device

    if(newdevice):
        phone = newdevice

    with open(recent_download, "rb") as song:
        file_data = pb.upload_file(song, recent_download)

    print("Now sending the file to {0}".format(phone))
    #push = pb.push_file(**file_data)

    if(delete):
        os.remove(recent_download)

@cli.command()
def initialise():
    """Initialise the program with yout API key and preffered device."""
    click.secho(
        "Please enter your API Key, you can obtain it from here: https://www.pushbullet.com/#settings/account",
        bold=True)
    api_key = input()

    click.secho(
        "Enter the serial number (eg 1 or 2) for your preffered device to send your music files",
        bold=True)

    pb = Pushbullet(api_key)

    i = 0
    for device in pb.devices:
        print("{0} : {1}".format(i + 1, device))
        i += 1

    device_id = int(input()) - 1

    if not os.path.exists('Music'):
        os.makedirs('Music')
    click.secho(
        "The music would be downloaded to {0}/Music".format(os.getcwd()))

    data = {}
    data['user'] = []
    data['user'].append({
        'api_key': api_key,
        'device': str(pb.devices[device_id]),
    })
    with open('data.txt', 'w') as outfile:
        json.dump(data, outfile)

    click.secho("Now you can run `moboff download` :) ", fg="green", bold=True)

if __name__ == '__main__':
    cli()
<|endoftext|>


<commit_before>import json

from nose.tools import eq_, raises
from mock import MagicMock, patch

from buffer.models.update import Update, PATHS

def test_update_retrieving():
  '''
    Test basic update retrieving based on update's id
  '''

  mocked_api = MagicMock()
  mocked_api.get.return_value = {
    'text': 'me',
    'id': 1
  }

  update = Update(api=mocked_api, id=1)

  mocked_api.get.assert_called_once_with(url='updates/1.json')
  eq_(update.api, mocked_api)
  eq_(update.text, 'me')

def test_update_interactions():
  '''
    Test basic analytics retrieving
  '''

  mocked_api = MagicMock()
  mocked_api.get.return_value = {'interactions': [{'replies': 3}]}

  update = Update(mocked_api, raw_response={'id': 1, 'text': 'hey'})

  eq_(update.interactions, [{'replies': 3}])
  mocked_api.get.assert_called_once_with(url='updates/1/interactions.json')

def test_update_edit():
  '''
    Test basic update editing
  '''

  mocked_api = MagicMock()
  mocked_api.post.return_value = {
      'update': {'id': 1, 'text': 'hey!'}
  }

  update = Update(mocked_api, raw_response={'id':1, 'text': 'ola!'})
  new_update = update.edit(text='hey!')

  assert_update = Update(mocked_api, raw_response={'id':1, 'text': 'hey!'})

  post_data = 'text=hey!&'
  mocked_api.post.assert_called_once_with(url='updates/1/update.json',
      data=post_data)
  eq_(new_update, assert_update)
<commit_msg>Test update editing with all params<commit_after>import json

from nose.tools import eq_, raises
from mock import MagicMock, patch

from buffer.models.update import Update, PATHS

def test_update_retrieving():
  '''
    Test basic update retrieving based on update's id
  '''

  mocked_api = MagicMock()
  mocked_api.get.return_value = {
    'text': 'me',
    'id': 1
  }

  update = Update(api=mocked_api, id=1)

  mocked_api.get.assert_called_once_with(url='updates/1.json')
  eq_(update.api, mocked_api)
  eq_(update.text, 'me')

def test_update_interactions():
  '''
    Test basic analytics retrieving
  '''

  mocked_api = MagicMock()
  mocked_api.get.return_value = {'interactions': [{'replies': 3}]}

  update = Update(mocked_api, raw_response={'id': 1, 'text': 'hey'})

  eq_(update.interactions, [{'replies': 3}])
  mocked_api.get.assert_called_once_with(url='updates/1/interactions.json')

def test_update_edit():
  '''
    Test basic update editing
  '''

  mocked_api = MagicMock()
  mocked_api.post.return_value = {
      'update': {'id': 1, 'text': 'hey!'}
  }

  update = Update(mocked_api, raw_response={'id':1, 'text': 'ola!'})
  new_update = update.edit(text='hey!')

  assert_update = Update(mocked_api, raw_response={'id':1, 'text': 'hey!'})

  post_data = 'text=hey!&'
  mocked_api.post.assert_called_once_with(url='updates/1/update.json',
      data=post_data)
  eq_(new_update, assert_update)

def test_update_edit_params():
  '''
    Test basic update editing with all the params
  '''

  mocked_api = MagicMock()
  mocked_api.post.return_value = {
      'update': {'id': 1, 'text': 'hey!'}
  }

  update = Update(mocked_api, raw_response={'id':1, 'text': 'ola!'})
  new_update = update.edit(text='hey!', media={'link':'w'}, utc="a", now=True)

  assert_update = Update(mocked_api, raw_response={'id':1, 'text': 'hey!'})

  post_data = 'text=hey!&now=True&utc=a&media[link]=w&'
  mocked_api.post.assert_called_once_with(url='updates/1/update.json',
      data=post_data)
  eq_(new_update, assert_update)

<|endoftext|>