<commit_before># -*- coding: utf-8 -*-

"""Notebook utilities."""

#------------------------------------------------------------------------------
# Imports
#------------------------------------------------------------------------------

import os
import os.path as op

from IPython.display import display_javascript, display_html

#------------------------------------------------------------------------------
# JavaScript and CSS utility
#------------------------------------------------------------------------------

def _to_abs_path(path):
    """Transform a path relative to the root of the phy package to an
    absolute path."""
    current_directory = op.dirname(op.realpath(__file__))
    root = op.join(current_directory, '../')
    return op.join(root, path)

def _read_file(path):
    """Read a text file specified with an absolute path."""
    with open(path, 'r') as f:
        return f.read()

def _inject_js(path):
    """Inject a JS file in the notebook.

    Parameters
    ----------

    path : str
        Absolute path to a .js file.

    """
    display_javascript(_read_file(path), raw=True)

def _inject_css(path):
    """Inject a CSS file in the notebook.

    Parameters
    ----------

    path : str
        Absolute path to a .css file.

    """
    css = _read_file(path)
    html = '<style type="text/css">\n{0:s}\n</style>'.format(css)
    display_html(html, raw=True)

def load_css(path):
    """Load a CSS file specified with a path relative to the root
    of the phy module."""
    _inject_css(_to_abs_path(path))

def load_static(js=True, css=False):
    """Load all JS and CSS files in the 'static/' folder."""
    static_dir = _to_abs_path('static/')
    files = os.listdir(static_dir)
    for file in files:
        if css and op.splitext(file)[1] == '.css':
            _inject_css(op.join(static_dir, file))
        if js and op.splitext(file)[1] == '.js':
            _inject_js(op.join(static_dir, file))

#------------------------------------------------------------------------------
# Event loop integration
#------------------------------------------------------------------------------

VISPY_BACKENDS = ('pyqt4',
                  'wx',
                  'ipynb_webgl')

def _enable_gui(shell, backend):
    """Enable IPython GUI event loop integration."""
    shell.run_line_magic('gui', backend)

def ipython_shell():
    """Return True if we are in IPython."""
    # Import IPython.
    try:
        import IPython
        from IPython import get_ipython
    except ImportError:
        raise ImportError("IPython is required.")
    if IPython.__version__ < '3':
        raise ImportError("IPython >= 3.0 is required.")
    # Get the IPython shell.
    shell = get_ipython()
    return shell

def enable_notebook(backend=None):
    """Enable notebook integration with the given backend for VisPy."""
    # TODO: unit tests
    if backend not in (None,) + VISPY_BACKENDS:
        raise ValueError("'backend' must be one of: "
                         "{0:s}".format(', '.join(VISPY_BACKENDS)))
    # Import VisPy.
    try:
        from vispy import app
    except ImportError:
        raise ImportError("VisPy is required in the notebook.")
    # Default backend.
    if backend is None:
        # TODO: user-level parameter
        backend = 'pyqt4'
    # Enable the VisPy backend.
    app.use_app(backend)
    # Enable IPython event loop integration.
    shell = ipython_shell()
    if shell is not None:
        if backend == 'pyqt4':
            _enable_gui(shell, 'qt')
        elif backend == 'wx':
            _enable_gui(shell, 'wx')
    # Load static JS and CSS files.
    load_static()
<commit_msg>utils: inject css/js in head<commit_after># -*- coding: utf-8 -*-

"""Notebook utilities."""

#------------------------------------------------------------------------------
# Imports
#------------------------------------------------------------------------------

import os
import os.path as op

from IPython.display import display_javascript, display_html

#------------------------------------------------------------------------------
# JavaScript and CSS utility
#------------------------------------------------------------------------------

def _to_abs_path(path):
    """Transform a path relative to the root of the phy package to an
    absolute path."""
    current_directory = op.dirname(op.realpath(__file__))
    root = op.join(current_directory, '../')
    return op.join(root, path)

def _to_web(path):
    current_directory = os.path.join(op.dirname(op.realpath(__file__)), "..", "..")
    return op.relpath(_to_abs_path(path), current_directory)

def _read_file(path):
    """Read a text file specified with an absolute path."""
    with open(path, 'r') as f:
        return f.read()

def _inject_js(path, async=True):
    """Inject a JS file in the notebook.

    Parameters
    ----------

    path : str
        Absolute path to a .js file.

    """
    if async:
        myjs = "$('head').append(\"<script> require(['/nbextensions/" + _to_web(path) + "']); </script>\")"
    else:
        myjs = "$('head').append(\"<script type='text/javascript' src='/nbextensions/" + _to_web(path) + "'/>\")"
    display_javascript(myjs, raw=True)

def _inject_css(path):
    """Inject a CSS file in the notebook.

    Parameters
    ----------

    path : str
        Absolute path to a .css file.

    """
    #inject in head so it does not disappear with the outputarea
    mycss = '$("head").append(\'<link rel="stylesheet" href="/nbextensions/' + _to_web(path) + '" type="text/css"/>\')'
    display_javascript(mycss, raw=True)

def load_js(path, async=False):
    """Load a CSS file specified with a path relative to the root
    of the phy module."""
    _inject_js(_to_abs_path(path), require=require)

def load_css(path):
    """Load a CSS file specified with a path relative to the root
    of the phy module."""
    _inject_css(_to_abs_path(path))

def load_static(js=True, css=False):
    """Load all JS and CSS files in the 'static/' folder."""
    static_dir = _to_abs_path('static/')
    files = os.listdir(static_dir)
    for file in files:
        if css and op.splitext(file)[1] == '.css':
            _inject_css(op.join(static_dir, file))
        if js and op.splitext(file)[1] == '.js':
            _inject_js(op.join(static_dir, file))

#------------------------------------------------------------------------------
# Event loop integration
#------------------------------------------------------------------------------

VISPY_BACKENDS = ('pyqt4',
                  'wx',
                  'ipynb_webgl')

def _enable_gui(shell, backend):
    """Enable IPython GUI event loop integration."""
    shell.run_line_magic('gui', backend)

def ipython_shell():
    """Return True if we are in IPython."""
    # Import IPython.
    try:
        import IPython
        from IPython import get_ipython
    except ImportError:
        raise ImportError("IPython is required.")
    if IPython.__version__ < '3':
        raise ImportError("IPython >= 3.0 is required.")
    # Get the IPython shell.
    shell = get_ipython()
    return shell

def enable_notebook(backend=None):
    """Enable notebook integration with the given backend for VisPy."""
    # TODO: unit tests
    if backend not in (None,) + VISPY_BACKENDS:
        raise ValueError("'backend' must be one of: "
                         "{0:s}".format(', '.join(VISPY_BACKENDS)))
    # Import VisPy.
    try:
        from vispy import app
    except ImportError:
        raise ImportError("VisPy is required in the notebook.")
    # Default backend.
    if backend is None:
        # TODO: user-level parameter
        backend = 'pyqt4'
    # Enable the VisPy backend.
    app.use_app(backend)
    # Enable IPython event loop integration.
    shell = ipython_shell()
    if shell is not None:
        if backend == 'pyqt4':
            _enable_gui(shell, 'qt')
        elif backend == 'wx':
            _enable_gui(shell, 'wx')
    # Load static JS and CSS files.
    load_static()
<|endoftext|>


<commit_before>import gtk
import pango

import os
import sys

from notebook import Notebook
from shell_buffer import ShellBuffer
from shell_view import ShellView

from format_escaped import format_escaped
from optparse import OptionParser

usage = "usage: %prog [options]"
op = OptionParser(usage=usage)
op.add_option("-u", "--ui", type="choice", choices=("standard", "hildon"),
              default="standard",  help=("which user interface to use (standard or "
					 "hildon), default=%default"))

options, args = op.parse_args()
use_hildon = False

if options.ui == "hildon":
    try:
        import hildon
        use_hildon = True
    except ImportError, e:
        print "Error importing hildon. Falling back to standard ui."

notebook = Notebook()

if use_hildon:
    w = hildon.Window()
else:
    w = gtk.Window()

v = gtk.VBox()
w.add(v)

buf = ShellBuffer(notebook)
view = ShellView(buf)
view.modify_font(pango.FontDescription("monospace"))
buf = view.get_buffer()

ui_manager = gtk.UIManager()
w.add_accel_group(ui_manager.get_accel_group())

def quit():
    if not confirm_discard('Save the unchanged changes to worksheet "%s" before quitting?', '_Quit without saving'):
        return
    gtk.main_quit()

def on_quit(action):
    quit()

def on_cut(action):
    buf.cut_clipboard(view.get_clipboard(gtk.gdk.SELECTION_CLIPBOARD), view.get_editable())

def on_copy(action):
    buf.copy_clipboard(view.get_clipboard(gtk.gdk.SELECTION_CLIPBOARD))

def on_paste(action):
    buf.paste_clipboard(view.get_clipboard(gtk.gdk.SELECTION_CLIPBOARD), None, view.get_editable())

def on_delete(action):
    buf.delete_selection(True, view.get_editable())

def confirm_discard(message_format, continue_button_text):
    if not buf.code_modified:
        return True

    if buf.filename == None:
        save_button_text = gtk.STOCK_SAVE_AS
    else:
        save_button_text = gtk.STOCK_SAVE

    if buf.filename == None:
        name = "Unsaved Worksheet"
    else:
        name = buf.filename

    message = format_escaped("<big>" + message_format + "</big>", name)

    dialog = gtk.MessageDialog(parent=w, buttons=gtk.BUTTONS_NONE,
                               type=gtk.MESSAGE_WARNING)
    dialog.set_markup(message)

    dialog.add_buttons(continue_button_text, gtk.RESPONSE_OK,
                       gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                       save_button_text, 1)
    dialog.set_default_response(1)
    response = dialog.run()
    dialog.destroy()

    if response == gtk.RESPONSE_OK:
        return True
    elif response == 1:
        if buf.filename == None:
            save_as()
        else:
            buf.save()

        if buf.code_modified:
            return False
        else:
            return True
    else:
        return False

def on_new(action):
    if not confirm_discard('Discard unsaved changes to worksheet "%s"?', '_Discard'):
        return

    buf.clear()

def load(filename):
    notebook.set_path([os.path.dirname(os.path.abspath(filename))])

    buf.load(filename)
    calculate()

def on_open(action):
    if not confirm_discard('Discard unsaved changes to worksheet "%s"?', '_Discard'):
        return

    if use_hildon:
        chooser = hildon.FileChooserDialog(w, gtk.FILE_CHOOSER_ACTION_OPEN)
    else:
        chooser = gtk.FileChooserDialog("Open Worksheet...", w, gtk.FILE_CHOOSER_ACTION_OPEN,
                                        (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                         gtk.STOCK_OPEN,   gtk.RESPONSE_OK))
    chooser.set_default_response(gtk.RESPONSE_OK)
    response = chooser.run()
    filename = None
    if response == gtk.RESPONSE_OK:
        filename = chooser.get_filename()

    if filename != None:
        load(filename)

    chooser.destroy()

def on_save(action):
    if buf.filename == None:
        on_save_as(action)
    else:
        buf.save()

def save_as():
    if use_hildon:
        chooser = hildon.FileChooserDialog(w, gtk.FILE_CHOOSER_ACTION_SAVE)
    else:
        chooser = gtk.FileChooserDialog("Save As...", w, gtk.FILE_CHOOSER_ACTION_SAVE,
                                        (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                         gtk.STOCK_SAVE,   gtk.RESPONSE_OK))
    chooser.set_default_response(gtk.RESPONSE_OK)
    response = chooser.run()
    filename = None
    if response == gtk.RESPONSE_OK:
        filename = chooser.get_filename()

    if filename != None:
        buf.save(filename)
        notebook.set_path([os.path.dirname(os.path.abspath(filename))])

    chooser.destroy()

def on_save_as(action):
    save_as()

def calculate():
    buf.calculate()

    # This is a hack to work around the fact that scroll_mark_onscreen()
    # doesn't wait for a size-allocate cycle, so doesn't properly handle
    # embedded request widgets
    w, h = view.size_request()
    view.size_allocate((view.allocation.x, view.allocation.y, w, h))

    view.scroll_mark_onscreen(buf.get_insert())

def on_calculate(action):
    calculate()

action_group = gtk.ActionGroup("main")
action_group.add_actions([
    ('file',    None,                "_File"),
    ('edit',    None,                "_Edit"),
    ('new',     gtk.STOCK_NEW,       None,         None,              None, on_new),
    ('open',    gtk.STOCK_OPEN,      None,         None,              None, on_open),
    ('save',    gtk.STOCK_SAVE,      None,         None,              None, on_save),
    ('save-as', gtk.STOCK_SAVE_AS,   None,         None,              None, on_save_as),
    ('quit',    gtk.STOCK_QUIT,      None,         None,              None, on_quit),
    ('cut',     gtk.STOCK_CUT,       None,         None,              None, on_cut),
    ('copy',    gtk.STOCK_COPY,      None,         None,              None, on_copy),
    ('paste',   gtk.STOCK_PASTE,     None,         None,              None, on_paste),
    ('delete',  gtk.STOCK_DELETE,    None,         None,              None, on_delete),
    ('calculate', gtk.STOCK_REFRESH, "_Calculate", '<control>Return', None, on_calculate),
])

ui_manager.insert_action_group(action_group, 0)

if use_hildon:
    menu_element = 'popup'
else:
    menu_element = 'menubar'

ui_string="""
<ui>
   <%(menu_element)s name="TopMenu">
      <menu action="file">
         <menuitem action="new"/>
         <menuitem action="open"/>
         <separator/>
         <menuitem action="save"/>
         <menuitem action="save-as"/>
         <separator/>
         <menuitem action="quit"/>
      </menu>
      <menu action="edit">
         <menuitem action="cut"/>
         <menuitem action="copy"/>
         <menuitem action="paste"/>
         <menuitem action="delete"/>
         <separator/>
         <menuitem action="calculate"/>
      </menu>
   </menubar>
   <toolbar name="ToolBar">
      <toolitem action="calculate"/>
   </toolbar>
</ui>
""" % { 'menu_element': menu_element }

ui_manager.add_ui_from_string(ui_string)
ui_manager.ensure_update()

menu = ui_manager.get_widget("/TopMenu")
toolbar = ui_manager.get_widget("/ToolBar")

if use_hildon:
    w.set_menu(menu)
    w.add_toolbar(toolbar)
else:
    v.pack_start(menu, expand=False, fill=False)
    v.pack_start(toolbar, expand=False, fill=False)

sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
v.pack_start(sw, expand=True, fill=True)

sw.add(view)

w.set_default_size(700, 800)

v.show_all()
view.grab_focus()

def update_title(*args):
    if buf.code_modified:
        title = "*"
    else:
        title = ""

    if buf.filename == None:
        title += "Unsaved Worksheet"
    else:
        title += os.path.basename(buf.filename)

    title += " - Reinteract"

    w.set_title(title)

buf.connect('filename-changed', update_title)
buf.connect('code-modified-changed', update_title)

update_title()

# We have a <Control>Return accelerator, but this hooks up <Control>KP_Enter as well;
# maybe someone wants that
def on_key_press_event(window, event):
    if (event.keyval == 0xff0d or event.keyval == 0xff8d) and (event.state & gtk.gdk.CONTROL_MASK != 0):
        calculate()
        return True
    return False

w.connect('key-press-event', on_key_press_event)

if len(args) > 0:
    load(args[0])

if use_hildon:
    settings = w.get_settings()
    settings.set_property("gtk-button-images", False)
    settings.set_property("gtk-menu-images", False)

w.show()

def on_delete_event(window, event):
    quit()
    return True

w.connect('delete-event', on_delete_event)

gtk.main()
<commit_msg>Fix mistake introduced when hildon UI patch was committed<commit_after>import gtk
import pango

import os
import sys

from notebook import Notebook
from shell_buffer import ShellBuffer
from shell_view import ShellView

from format_escaped import format_escaped
from optparse import OptionParser

usage = "usage: %prog [options]"
op = OptionParser(usage=usage)
op.add_option("-u", "--ui", type="choice", choices=("standard", "hildon"),
              default="standard",  help=("which user interface to use (standard or "
					 "hildon), default=%default"))

options, args = op.parse_args()
use_hildon = False

if options.ui == "hildon":
    try:
        import hildon
        use_hildon = True
    except ImportError, e:
        print "Error importing hildon. Falling back to standard ui."

notebook = Notebook()

if use_hildon:
    w = hildon.Window()
else:
    w = gtk.Window()

v = gtk.VBox()
w.add(v)

buf = ShellBuffer(notebook)
view = ShellView(buf)
view.modify_font(pango.FontDescription("monospace"))
buf = view.get_buffer()

ui_manager = gtk.UIManager()
w.add_accel_group(ui_manager.get_accel_group())

def quit():
    if not confirm_discard('Save the unchanged changes to worksheet "%s" before quitting?', '_Quit without saving'):
        return
    gtk.main_quit()

def on_quit(action):
    quit()

def on_cut(action):
    buf.cut_clipboard(view.get_clipboard(gtk.gdk.SELECTION_CLIPBOARD), view.get_editable())

def on_copy(action):
    buf.copy_clipboard(view.get_clipboard(gtk.gdk.SELECTION_CLIPBOARD))

def on_paste(action):
    buf.paste_clipboard(view.get_clipboard(gtk.gdk.SELECTION_CLIPBOARD), None, view.get_editable())

def on_delete(action):
    buf.delete_selection(True, view.get_editable())

def confirm_discard(message_format, continue_button_text):
    if not buf.code_modified:
        return True

    if buf.filename == None:
        save_button_text = gtk.STOCK_SAVE_AS
    else:
        save_button_text = gtk.STOCK_SAVE

    if buf.filename == None:
        name = "Unsaved Worksheet"
    else:
        name = buf.filename

    message = format_escaped("<big>" + message_format + "</big>", name)

    dialog = gtk.MessageDialog(parent=w, buttons=gtk.BUTTONS_NONE,
                               type=gtk.MESSAGE_WARNING)
    dialog.set_markup(message)

    dialog.add_buttons(continue_button_text, gtk.RESPONSE_OK,
                       gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                       save_button_text, 1)
    dialog.set_default_response(1)
    response = dialog.run()
    dialog.destroy()

    if response == gtk.RESPONSE_OK:
        return True
    elif response == 1:
        if buf.filename == None:
            save_as()
        else:
            buf.save()

        if buf.code_modified:
            return False
        else:
            return True
    else:
        return False

def on_new(action):
    if not confirm_discard('Discard unsaved changes to worksheet "%s"?', '_Discard'):
        return

    buf.clear()

def load(filename):
    notebook.set_path([os.path.dirname(os.path.abspath(filename))])

    buf.load(filename)
    calculate()

def on_open(action):
    if not confirm_discard('Discard unsaved changes to worksheet "%s"?', '_Discard'):
        return

    if use_hildon:
        chooser = hildon.FileChooserDialog(w, gtk.FILE_CHOOSER_ACTION_OPEN)
    else:
        chooser = gtk.FileChooserDialog("Open Worksheet...", w, gtk.FILE_CHOOSER_ACTION_OPEN,
                                        (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                         gtk.STOCK_OPEN,   gtk.RESPONSE_OK))
    chooser.set_default_response(gtk.RESPONSE_OK)
    response = chooser.run()
    filename = None
    if response == gtk.RESPONSE_OK:
        filename = chooser.get_filename()

    if filename != None:
        load(filename)

    chooser.destroy()

def on_save(action):
    if buf.filename == None:
        on_save_as(action)
    else:
        buf.save()

def save_as():
    if use_hildon:
        chooser = hildon.FileChooserDialog(w, gtk.FILE_CHOOSER_ACTION_SAVE)
    else:
        chooser = gtk.FileChooserDialog("Save As...", w, gtk.FILE_CHOOSER_ACTION_SAVE,
                                        (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                         gtk.STOCK_SAVE,   gtk.RESPONSE_OK))
    chooser.set_default_response(gtk.RESPONSE_OK)
    response = chooser.run()
    filename = None
    if response == gtk.RESPONSE_OK:
        filename = chooser.get_filename()

    if filename != None:
        buf.save(filename)
        notebook.set_path([os.path.dirname(os.path.abspath(filename))])

    chooser.destroy()

def on_save_as(action):
    save_as()

def calculate():
    buf.calculate()

    # This is a hack to work around the fact that scroll_mark_onscreen()
    # doesn't wait for a size-allocate cycle, so doesn't properly handle
    # embedded request widgets
    w, h = view.size_request()
    view.size_allocate((view.allocation.x, view.allocation.y, w, h))

    view.scroll_mark_onscreen(buf.get_insert())

def on_calculate(action):
    calculate()

action_group = gtk.ActionGroup("main")
action_group.add_actions([
    ('file',    None,                "_File"),
    ('edit',    None,                "_Edit"),
    ('new',     gtk.STOCK_NEW,       None,         None,              None, on_new),
    ('open',    gtk.STOCK_OPEN,      None,         None,              None, on_open),
    ('save',    gtk.STOCK_SAVE,      None,         None,              None, on_save),
    ('save-as', gtk.STOCK_SAVE_AS,   None,         None,              None, on_save_as),
    ('quit',    gtk.STOCK_QUIT,      None,         None,              None, on_quit),
    ('cut',     gtk.STOCK_CUT,       None,         None,              None, on_cut),
    ('copy',    gtk.STOCK_COPY,      None,         None,              None, on_copy),
    ('paste',   gtk.STOCK_PASTE,     None,         None,              None, on_paste),
    ('delete',  gtk.STOCK_DELETE,    None,         None,              None, on_delete),
    ('calculate', gtk.STOCK_REFRESH, "_Calculate", '<control>Return', None, on_calculate),
])

ui_manager.insert_action_group(action_group, 0)

if use_hildon:
    menu_element = 'popup'
else:
    menu_element = 'menubar'

ui_string="""
<ui>
   <%(menu_element)s name="TopMenu">
      <menu action="file">
         <menuitem action="new"/>
         <menuitem action="open"/>
         <separator/>
         <menuitem action="save"/>
         <menuitem action="save-as"/>
         <separator/>
         <menuitem action="quit"/>
      </menu>
      <menu action="edit">
         <menuitem action="cut"/>
         <menuitem action="copy"/>
         <menuitem action="paste"/>
         <menuitem action="delete"/>
         <separator/>
         <menuitem action="calculate"/>
      </menu>
   </%(menu_element)s>
   <toolbar name="ToolBar">
      <toolitem action="calculate"/>
   </toolbar>
</ui>
""" % { 'menu_element': menu_element }

ui_manager.add_ui_from_string(ui_string)
ui_manager.ensure_update()

menu = ui_manager.get_widget("/TopMenu")
toolbar = ui_manager.get_widget("/ToolBar")

if use_hildon:
    w.set_menu(menu)
    w.add_toolbar(toolbar)
else:
    v.pack_start(menu, expand=False, fill=False)
    v.pack_start(toolbar, expand=False, fill=False)

sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
v.pack_start(sw, expand=True, fill=True)

sw.add(view)

w.set_default_size(700, 800)

v.show_all()
view.grab_focus()

def update_title(*args):
    if buf.code_modified:
        title = "*"
    else:
        title = ""

    if buf.filename == None:
        title += "Unsaved Worksheet"
    else:
        title += os.path.basename(buf.filename)

    title += " - Reinteract"

    w.set_title(title)

buf.connect('filename-changed', update_title)
buf.connect('code-modified-changed', update_title)

update_title()

# We have a <Control>Return accelerator, but this hooks up <Control>KP_Enter as well;
# maybe someone wants that
def on_key_press_event(window, event):
    if (event.keyval == 0xff0d or event.keyval == 0xff8d) and (event.state & gtk.gdk.CONTROL_MASK != 0):
        calculate()
        return True
    return False

w.connect('key-press-event', on_key_press_event)

if len(args) > 0:
    load(args[0])

if use_hildon:
    settings = w.get_settings()
    settings.set_property("gtk-button-images", False)
    settings.set_property("gtk-menu-images", False)

w.show()

def on_delete_event(window, event):
    quit()
    return True

w.connect('delete-event', on_delete_event)

gtk.main()
<|endoftext|>


<commit_before>from eventbus import EventBus
from laser import Laser
from navigator import Navigator
from protocol import CMD_TURN_STARTED, CMD_TURN_FINISHED
from section import Section, NORTH, EAST, SOUTH, WEST, BLOCK_LENGTH_MM
from threading import Thread

STATE_MEASURING = 0
STATE_WAITING = 1
STATE_FINISHED = 2

MAPPING_STATE_FOLLOWING_OUTER_WALL = 0
MAPPING_STATE_RETURNING_TO_ISLAND = 1
MAPPING_STATE_FOLLOWING_ISLAND = 2
MAPPING_STATE_RETURNING_TO_GARAGE = 3

class Position:
    def __init__(self, laser, ir, navigator):
        self.laser = laser
        self.ir = ir
        self.navigator = navigator
        self.state = STATE_MEASURING
        self.mapping_state = MAPPING_STATE_FOLLOWING_OUTER_WALL
        self.current_section = Section(NORTH)
        self.saved_sections = []
        self.map_data = [(0, 0)]
        self.current_x = 0
        self.current_y = 0
        self.kitchen_start_x = 0
        self.kitchen_start_y = 0
        self.kitchen_num_mapped = 0
        self.num_kitchen_turns = 0

        self.kitchen_mapping = {}
        self.invalid_kitchens = []
        self.island_data = []

        self.num_ok_close = 0
        self.num_ok_far = 0

        EventBus.subscribe(CMD_TURN_STARTED, self.on_turning_started)
        EventBus.subscribe(CMD_TURN_FINISHED, self.on_turning_finished)

    def update(self):
        if self.state == STATE_MEASURING:
            distance = self.laser.get_data()
            self.current_section.add_distance_sample(distance)
            if self.mapping_state == MAPPING_STATE_FOLLOWING_OUTER_WALL:
                self.looking_for_kitchen_sections()
                self.remove_invalid_kitchen_sections()
            elif self.mapping_state == MAPPING_STATE_RETURNING_TO_ISLAND:
                self.check_if_returned_to_island()
            elif self.mapping_state == MAPPING_STATE_FOLLOWING_ISLAND:
                self.current_section.finish()
                temporary_x, temporary_y = self.transform_map_data(self.current_section, self.current_x, self.current_y, estimate=True, offset=0.1)
                if temporary_x == self.kitchen_start_x and temporary_y == self.kitchen_start_y \
                        and self.kitchen_num_mapped > 3:
                    self.map_island_data()
                    self.map_remaining_data()

                    Navigator.force_left_turn = True

                    # Last island section is not properly stored because of a
                    # turn so we must store it here
                    self.store_ongoing_section()

                    self.mapping_state = MAPPING_STATE_RETURNING_TO_GARAGE

    def looking_for_kitchen_sections(self):
        if self.has_close_kitchen_left():
            self.add_kitchen_mapping(1, 0.5)

        if self.has_far_kitchen_left():
            self.add_kitchen_mapping(2, 0.25)

    def has_close_kitchen_left(self):
        match = self.ir.get_ir_left() > 10
        if match:
            self.num_ok_close += 1
        else:
            self.num_ok_close = 0

        if self.num_ok_close > 7:
            self.num_ok_close = 0
            return True
        else:
            return False

    def has_far_kitchen_left(self):
        match = 250 < self.ir.get_ir_left_back() < 650
        if match:
            self.num_ok_far += 1
        else:
            self.num_ok_far = 0

        if self.num_ok_far > 7:
            self.num_ok_far = 0
            return True
        else:
            return False

    def add_kitchen_mapping(self, displacement, offset):
        distance = self.current_section.estimate_block_distance(offset)
        cur_x, cur_y = self.transform_partial_map_data(distance, self.current_section.direction, self.current_x, self.current_y)
        key = self.get_left_block_coordinates(cur_x, cur_y, displacement)

        if key not in self.kitchen_mapping and key not in self.invalid_kitchens:
            print('Adding new mapping (' + str(key[0]) + ', ' + str(key[1]) + ') -> (' + str(cur_x) + ', ' + str(cur_y) + ')')
            self.kitchen_mapping[key] = (cur_x, cur_y)

    def remove_invalid_kitchen_sections(self):
        temporary_x, temporary_y = self.transform_map_data(self.current_section,
                                                           self.current_x,
                                                           self.current_y)
        key = self.get_right_block_coordinates(temporary_x, temporary_y, 1)
        popped = self.kitchen_mapping.pop(key, None)
        if popped is not None:
            print('Removed kitchen mapping for block (' + str(key[0]) + ', ' + str(key[1]) + ')')

        if key not in self.invalid_kitchens:
            print('Forbidding kitchen block at ' + str(key))
            self.invalid_kitchens.append(key)

    def check_if_returned_to_island(self):
        coordinates = self.transform_map_data(self.current_section,
                                              self.current_x,
                                              self.current_y,
                                              estimate=True)

        if coordinates in self.kitchen_mapping.values():
            print('Starting to map island!')

            self.mapping_state = MAPPING_STATE_FOLLOWING_ISLAND
            self.kitchen_num_mapped = 0
            self.num_kitchen_turns = 0
            key1 = self.transform_partial_map_data(1, self.current_section.direction, coordinates[0], coordinates[1])
            key2 = self.transform_partial_map_data(2, self.current_section.direction, coordinates[0], coordinates[1])
            if key1 not in self.kitchen_mapping and key2 not in self.kitchen_mapping:
                Navigator.force_left_turn = True
            else:
                Navigator.right_turn_enabled = False
                self.num_kitchen_turns = 1

    def map_island_data(self):
        if len(self.island_data) == 0:
            print('ERROR: No island to map!')
            return

        initial_island_data = list(self.island_data)
        for pos in initial_island_data:
            self.go_deeper(pos[0], pos[1], self.island_data, self.map_data)

    def map_remaining_data(self):
        initial_map_data = list(self.map_data)
        for pos in initial_map_data:
            self.go_deeper(pos[0], pos[1], self.map_data, self.island_data)

    def go_deeper(self, x, y, lst, avoid_lst):
        pos = (x, y)
        if pos not in avoid_lst:
            if pos not in lst:
                lst.append(pos)

                try:
                    self.go_deeper(x + 1, y, lst)
                    self.go_deeper(x - 1, y, lst)
                    self.go_deeper(x, y + 1, lst)
                    self.go_deeper(x, y - 1, lst)
                except:
                    print('Your island or map is too big')

    def save_current_section(self):
        if self.mapping_state == MAPPING_STATE_FOLLOWING_OUTER_WALL:
            self.process_finished_section()

            print('---- SECTION SAVED ----')
            print('  direction: ' + str(self.current_section.direction))
            print('  distance: ' + str(self.current_section.block_distance))
            print('  coordinates: ' + str(self.current_x) + ", " + str(self.current_y))
            print('  kitchen mappings: \n' + self.get_kitchen_debug_data())
            print('-----------------------')
        elif self.mapping_state == MAPPING_STATE_FOLLOWING_ISLAND:
            self.process_finished_section()

            print('---- ISLAND ROUNDED ----')
            print('  direction: ' + str(self.current_section.direction))
            print('  distance: ' + str(self.current_section.block_distance))
            print('  coordinates: ' + str(self.current_x) + ", " + str(self.current_y))
            print('-----------------------')

            self.kitchen_num_mapped += 1
            self.num_kitchen_turns += 1
        else:
            self.process_finished_section(store_data=False)

            print('---- SECTION TRAVELLED ----')
            print('  direction: ' + str(self.current_section.direction))
            print('  distance: ' + str(self.current_section.block_distance))
            print('  coordinates: ' + str(self.current_x) + ", " + str(self.current_y))
            print('-----------------------')

    def get_kitchen_debug_data(self):
        data = []
        for k, v in self.kitchen_mapping.items():
            data.append('    ' + str(k) + ' -> ' + str(v))

        return '\n'.join(data)

    def begin_next_section(self, is_right_turn):
        if is_right_turn:
            self.current_section = self.current_section.for_right_turn()
        else:
            self.current_section = self.current_section.for_left_turn()

    def on_turning_started(self):
        if self.mapping_state == MAPPING_STATE_RETURNING_TO_GARAGE:
            if self.has_returned_to_start(include_direction=False):
                self.state = STATE_FINISHED
                self.navigator.set_mode(Navigator.MANUAL)
                print(self.map_data)
                print(self.island_data)
        else:
            self.state = STATE_WAITING

        self.save_current_section()

    def on_turning_finished(self, is_right_turn):
        self.begin_next_section(is_right_turn)
        if self.has_returned_to_start() or self.mapping_state == MAPPING_STATE_RETURNING_TO_ISLAND:
            self.mapping_state = MAPPING_STATE_RETURNING_TO_ISLAND
            self.island_data = list(self.kitchen_mapping.values())

        if self.mapping_state == MAPPING_STATE_FOLLOWING_ISLAND and self.num_kitchen_turns == 2:
            self.kitchen_start_x = self.current_x
            self.kitchen_start_y = self.current_y

        self.state = STATE_MEASURING

    def has_returned_to_start(self, include_direction=True):

        if self.current_x == 0 and self.current_y == 0 and len(self.map_data) > 0:
            return not include_direction or self.current_section.direction == NORTH
        else:
            return False

    def transform_map_data(self, section, current_x, current_y, estimate=False, offset=None):
        if estimate:
            if offset is None:
                distance = section.estimate_block_distance()
            else:
                distance = section.estimate_block_distance(offset)
        else:
            section.finish()
            distance = section.block_distance

        return self.transform_partial_map_data(distance, section.direction, current_x, current_y)

    def transform_partial_map_data(self, distance, direction, current_x, current_y):
        if direction == NORTH:
            current_y += distance
        elif direction == EAST:
            current_x += distance
        elif direction == SOUTH:
            current_y -= distance
        elif direction == WEST:
            current_x -= distance
        return current_x, current_y

    def store_ongoing_section(self):
        self.current_section.finish()

        distance = self.current_section.block_distance
        direction = self.current_section.direction

        for i in range(1, distance + 1):
            pos_x, pos_y = self.transform_partial_map_data(i, direction, self.current_x, self.current_y)
            self.map_data.append((pos_x, pos_y))

    def process_finished_section(self, store_data=True):
        self.current_section.finish(debug_limits=True)

        if store_data:
            distance = self.current_section.block_distance
            direction = self.current_section.direction

            for i in range(1, distance + 1):
                coordinates = self.transform_partial_map_data(i, direction, self.current_x, self.current_y)
                self.map_data.append(coordinates)
                self.kitchen_mapping.pop(coordinates, None)
                if coordinates not in self.invalid_kitchens:
                    self.invalid_kitchens.append(coordinates)

            self.saved_sections.append(self.current_section)

        # Could be optimized to use last pos_x, pos_y instead
        self.current_x, self.current_y = self.transform_map_data(self.current_section, self.current_x, self.current_y)

    '''
    Returns a list of tuples with coordinates of grids visited
    '''
    def get_map_data(self):
        return [self.map_data]

    '''
    Returns the robots last know x,y coordinates
    '''
    def get_current_position(self):
        return self.current_x, self.current_y

    def get_left_block_coordinates(self, x, y, displacement):
        if self.current_section.direction == NORTH:
            x -= displacement
        elif self.current_section.direction == EAST:
            y += displacement
        elif self.current_section.direction == SOUTH:
            x += displacement
        elif self.current_section.direction == WEST:
            y -= displacement

        return x, y

    def get_right_block_coordinates(self, x, y, displacement):
        return self.get_left_block_coordinates(x, y, -displacement)
<commit_msg>Print data after island is mapped<commit_after>from eventbus import EventBus
from laser import Laser
from navigator import Navigator
from protocol import CMD_TURN_STARTED, CMD_TURN_FINISHED
from section import Section, NORTH, EAST, SOUTH, WEST, BLOCK_LENGTH_MM
from threading import Thread

STATE_MEASURING = 0
STATE_WAITING = 1
STATE_FINISHED = 2

MAPPING_STATE_FOLLOWING_OUTER_WALL = 0
MAPPING_STATE_RETURNING_TO_ISLAND = 1
MAPPING_STATE_FOLLOWING_ISLAND = 2
MAPPING_STATE_RETURNING_TO_GARAGE = 3

class Position:
    def __init__(self, laser, ir, navigator):
        self.laser = laser
        self.ir = ir
        self.navigator = navigator
        self.state = STATE_MEASURING
        self.mapping_state = MAPPING_STATE_FOLLOWING_OUTER_WALL
        self.current_section = Section(NORTH)
        self.saved_sections = []
        self.map_data = [(0, 0)]
        self.current_x = 0
        self.current_y = 0
        self.kitchen_start_x = 0
        self.kitchen_start_y = 0
        self.kitchen_num_mapped = 0
        self.num_kitchen_turns = 0

        self.kitchen_mapping = {}
        self.invalid_kitchens = []
        self.island_data = []

        self.num_ok_close = 0
        self.num_ok_far = 0

        EventBus.subscribe(CMD_TURN_STARTED, self.on_turning_started)
        EventBus.subscribe(CMD_TURN_FINISHED, self.on_turning_finished)

    def update(self):
        if self.state == STATE_MEASURING:
            distance = self.laser.get_data()
            self.current_section.add_distance_sample(distance)
            if self.mapping_state == MAPPING_STATE_FOLLOWING_OUTER_WALL:
                self.looking_for_kitchen_sections()
                self.remove_invalid_kitchen_sections()
            elif self.mapping_state == MAPPING_STATE_RETURNING_TO_ISLAND:
                self.check_if_returned_to_island()
            elif self.mapping_state == MAPPING_STATE_FOLLOWING_ISLAND:
                self.current_section.finish()
                temporary_x, temporary_y = self.transform_map_data(self.current_section, self.current_x, self.current_y, estimate=True, offset=0.1)
                if temporary_x == self.kitchen_start_x and temporary_y == self.kitchen_start_y \
                        and self.kitchen_num_mapped > 3:
                    self.map_island_data()
                    self.map_remaining_data()

                    print(self.map_data)
                    print(self.island_data)

                    Navigator.force_left_turn = True

                    # Last island section is not properly stored because of a
                    # turn so we must store it here
                    self.store_ongoing_section()

                    self.mapping_state = MAPPING_STATE_RETURNING_TO_GARAGE

    def looking_for_kitchen_sections(self):
        if self.has_close_kitchen_left():
            self.add_kitchen_mapping(1, 0.5)

        if self.has_far_kitchen_left():
            self.add_kitchen_mapping(2, 0.25)

    def has_close_kitchen_left(self):
        match = self.ir.get_ir_left() > 10
        if match:
            self.num_ok_close += 1
        else:
            self.num_ok_close = 0

        if self.num_ok_close > 7:
            self.num_ok_close = 0
            return True
        else:
            return False

    def has_far_kitchen_left(self):
        match = 250 < self.ir.get_ir_left_back() < 650
        if match:
            self.num_ok_far += 1
        else:
            self.num_ok_far = 0

        if self.num_ok_far > 7:
            self.num_ok_far = 0
            return True
        else:
            return False

    def add_kitchen_mapping(self, displacement, offset):
        distance = self.current_section.estimate_block_distance(offset)
        cur_x, cur_y = self.transform_partial_map_data(distance, self.current_section.direction, self.current_x, self.current_y)
        key = self.get_left_block_coordinates(cur_x, cur_y, displacement)

        if key not in self.kitchen_mapping and key not in self.invalid_kitchens:
            print('Adding new mapping (' + str(key[0]) + ', ' + str(key[1]) + ') -> (' + str(cur_x) + ', ' + str(cur_y) + ')')
            self.kitchen_mapping[key] = (cur_x, cur_y)

    def remove_invalid_kitchen_sections(self):
        temporary_x, temporary_y = self.transform_map_data(self.current_section,
                                                           self.current_x,
                                                           self.current_y)
        key = self.get_right_block_coordinates(temporary_x, temporary_y, 1)
        popped = self.kitchen_mapping.pop(key, None)
        if popped is not None:
            print('Removed kitchen mapping for block (' + str(key[0]) + ', ' + str(key[1]) + ')')

        if key not in self.invalid_kitchens:
            print('Forbidding kitchen block at ' + str(key))
            self.invalid_kitchens.append(key)

    def check_if_returned_to_island(self):
        coordinates = self.transform_map_data(self.current_section,
                                              self.current_x,
                                              self.current_y,
                                              estimate=True)

        if coordinates in self.kitchen_mapping.values():
            print('Starting to map island!')

            self.mapping_state = MAPPING_STATE_FOLLOWING_ISLAND
            self.kitchen_num_mapped = 0
            self.num_kitchen_turns = 0
            key1 = self.transform_partial_map_data(1, self.current_section.direction, coordinates[0], coordinates[1])
            key2 = self.transform_partial_map_data(2, self.current_section.direction, coordinates[0], coordinates[1])
            if key1 not in self.kitchen_mapping and key2 not in self.kitchen_mapping:
                Navigator.force_left_turn = True
            else:
                Navigator.right_turn_enabled = False
                self.num_kitchen_turns = 1

    def map_island_data(self):
        if len(self.island_data) == 0:
            print('ERROR: No island to map!')
            return

        initial_island_data = list(self.island_data)
        for pos in initial_island_data:
            self.go_deeper(pos[0], pos[1], self.island_data, self.map_data)

    def map_remaining_data(self):
        initial_map_data = list(self.map_data)
        for pos in initial_map_data:
            self.go_deeper(pos[0], pos[1], self.map_data, self.island_data)

    def go_deeper(self, x, y, lst, avoid_lst):
        pos = (x, y)
        if pos not in avoid_lst:
            if pos not in lst:
                lst.append(pos)

                try:
                    self.go_deeper(x + 1, y, lst)
                    self.go_deeper(x - 1, y, lst)
                    self.go_deeper(x, y + 1, lst)
                    self.go_deeper(x, y - 1, lst)
                except:
                    print('Your island or map is too big')

    def save_current_section(self):
        if self.mapping_state == MAPPING_STATE_FOLLOWING_OUTER_WALL:
            self.process_finished_section()

            print('---- SECTION SAVED ----')
            print('  direction: ' + str(self.current_section.direction))
            print('  distance: ' + str(self.current_section.block_distance))
            print('  coordinates: ' + str(self.current_x) + ", " + str(self.current_y))
            print('  kitchen mappings: \n' + self.get_kitchen_debug_data())
            print('-----------------------')
        elif self.mapping_state == MAPPING_STATE_FOLLOWING_ISLAND:
            self.process_finished_section()

            print('---- ISLAND ROUNDED ----')
            print('  direction: ' + str(self.current_section.direction))
            print('  distance: ' + str(self.current_section.block_distance))
            print('  coordinates: ' + str(self.current_x) + ", " + str(self.current_y))
            print('-----------------------')

            self.kitchen_num_mapped += 1
            self.num_kitchen_turns += 1
        else:
            self.process_finished_section(store_data=False)

            print('---- SECTION TRAVELLED ----')
            print('  direction: ' + str(self.current_section.direction))
            print('  distance: ' + str(self.current_section.block_distance))
            print('  coordinates: ' + str(self.current_x) + ", " + str(self.current_y))
            print('-----------------------')

    def get_kitchen_debug_data(self):
        data = []
        for k, v in self.kitchen_mapping.items():
            data.append('    ' + str(k) + ' -> ' + str(v))

        return '\n'.join(data)

    def begin_next_section(self, is_right_turn):
        if is_right_turn:
            self.current_section = self.current_section.for_right_turn()
        else:
            self.current_section = self.current_section.for_left_turn()

    def on_turning_started(self):
        if self.mapping_state == MAPPING_STATE_RETURNING_TO_GARAGE:
            if self.has_returned_to_start(include_direction=False):
                self.state = STATE_FINISHED
                self.navigator.set_mode(Navigator.MANUAL)
                print(self.map_data)
                print(self.island_data)
        else:
            self.state = STATE_WAITING

        self.save_current_section()

    def on_turning_finished(self, is_right_turn):
        self.begin_next_section(is_right_turn)
        if self.has_returned_to_start() or self.mapping_state == MAPPING_STATE_RETURNING_TO_ISLAND:
            self.mapping_state = MAPPING_STATE_RETURNING_TO_ISLAND
            self.island_data = list(self.kitchen_mapping.values())

        if self.mapping_state == MAPPING_STATE_FOLLOWING_ISLAND and self.num_kitchen_turns == 2:
            self.kitchen_start_x = self.current_x
            self.kitchen_start_y = self.current_y

        self.state = STATE_MEASURING

    def has_returned_to_start(self, include_direction=True):

        if self.current_x == 0 and self.current_y == 0 and len(self.map_data) > 0:
            return not include_direction or self.current_section.direction == NORTH
        else:
            return False

    def transform_map_data(self, section, current_x, current_y, estimate=False, offset=None):
        if estimate:
            if offset is None:
                distance = section.estimate_block_distance()
            else:
                distance = section.estimate_block_distance(offset)
        else:
            section.finish()
            distance = section.block_distance

        return self.transform_partial_map_data(distance, section.direction, current_x, current_y)

    def transform_partial_map_data(self, distance, direction, current_x, current_y):
        if direction == NORTH:
            current_y += distance
        elif direction == EAST:
            current_x += distance
        elif direction == SOUTH:
            current_y -= distance
        elif direction == WEST:
            current_x -= distance
        return current_x, current_y

    def store_ongoing_section(self):
        self.current_section.finish()

        distance = self.current_section.block_distance
        direction = self.current_section.direction

        for i in range(1, distance + 1):
            pos_x, pos_y = self.transform_partial_map_data(i, direction, self.current_x, self.current_y)
            self.map_data.append((pos_x, pos_y))

    def process_finished_section(self, store_data=True):
        self.current_section.finish(debug_limits=True)

        if store_data:
            distance = self.current_section.block_distance
            direction = self.current_section.direction

            for i in range(1, distance + 1):
                coordinates = self.transform_partial_map_data(i, direction, self.current_x, self.current_y)
                self.map_data.append(coordinates)
                self.kitchen_mapping.pop(coordinates, None)
                if coordinates not in self.invalid_kitchens:
                    self.invalid_kitchens.append(coordinates)

            self.saved_sections.append(self.current_section)

        # Could be optimized to use last pos_x, pos_y instead
        self.current_x, self.current_y = self.transform_map_data(self.current_section, self.current_x, self.current_y)

    '''
    Returns a list of tuples with coordinates of grids visited
    '''
    def get_map_data(self):
        return [self.map_data]

    '''
    Returns the robots last know x,y coordinates
    '''
    def get_current_position(self):
        return self.current_x, self.current_y

    def get_left_block_coordinates(self, x, y, displacement):
        if self.current_section.direction == NORTH:
            x -= displacement
        elif self.current_section.direction == EAST:
            y += displacement
        elif self.current_section.direction == SOUTH:
            x += displacement
        elif self.current_section.direction == WEST:
            y -= displacement

        return x, y

    def get_right_block_coordinates(self, x, y, displacement):
        return self.get_left_block_coordinates(x, y, -displacement)
<|endoftext|>


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

""" PickleShare - a small 'shelve' like datastore with concurrency support

Like shelve, a PickleShareDB object acts like a normal dictionary. Unlike
shelve, many processes can access the database simultaneously. Changing a
value in database is immediately visible to other processes accessing the
same database.

Concurrency is possible because the values are stored in separate files. Hence
the "database" is a directory where *all* files are governed by PickleShare.

Example usage::

    from pickleshare import *
    db = PickleShareDB('~/testpickleshare')
    db.clear()
    print "Should be empty:",db.items()
    db['hello'] = 15
    db['aku ankka'] = [1,2,313]
    db['paths/are/ok/key'] = [1,(5,46)]
    print db.keys()
    del db['aku ankka']

This module is certainly not ZODB, but can be used for low-load
(non-mission-critical) situations where tiny code size trumps the
advanced features of a "real" object database.

Installation guide: pip install path pickleshare

Author: Ville Vainio <vivainio@gmail.com>
License: MIT open source license.

"""

from __future__ import print_function

__version__ = "0.7.3"

try:
    from pathlib import Path
except ImportError:
    # Python 2 backport
    from pathlib2 import Path

import os,stat,time
import collections
try:
    import cPickle as pickle
except ImportError:
    import pickle
import errno

def gethashfile(key):
    return ("%02x" % abs(hash(key) % 256))[-2:]

_sentinel = object()

class PickleShareDB(collections.MutableMapping):
    """ The main 'connection' object for PickleShare database """
    def __init__(self,root):
        """ Return a db object that will manage the specied directory"""
        root = os.path.abspath(os.path.expanduser(str(root)))
        self.root = Path(root)
        if not self.root.is_dir():
            # catching the exception is necessary if multiple processes are concurrently trying to create a folder
            # exists_ok keyword argument of mkdir does the same but only from Python 3.5
            try:
                self.root.mkdir(parents=True)
            except OSError as e:
                if e.errno != errno.EEXIST:
                    raise
        # cache has { 'key' : (obj, orig_mod_time) }
        self.cache = {}

    def __getitem__(self,key):
        """ db['key'] reading """
        fil = self.root / key
        try:
            mtime = (fil.stat()[stat.ST_MTIME])
        except OSError:
            raise KeyError(key)

        if fil in self.cache and mtime == self.cache[fil][1]:
            return self.cache[fil][0]
        try:
            # The cached item has expired, need to read
            with fil.open("rb") as f:
                obj = pickle.loads(f.read())
        except:
            raise KeyError(key)

        self.cache[fil] = (obj,mtime)
        return obj

    def __setitem__(self,key,value):
        """ db['key'] = 5 """
        fil = self.root / key
        parent = fil.parent
        if parent and not parent.is_dir():
            parent.mkdir(parents=True)
        # We specify protocol 2, so that we can mostly go between Python 2
        # and Python 3. We can upgrade to protocol 3 when Python 2 is obsolete.
        with fil.open('wb') as f:
            pickle.dump(value, f, protocol=2)
        try:
            self.cache[fil] = (value, fil.stat().st_mtime)
        except OSError as e:
            if e.errno != errno.ENOENT:
                raise

    def hset(self, hashroot, key, value):
        """ hashed set """
        hroot = self.root / hashroot
        if not hroot.is_dir():
            hroot.mkdir()
        hfile = hroot / gethashfile(key)
        d = self.get(hfile, {})
        d.update( {key : value})
        self[hfile] = d

    def hget(self, hashroot, key, default = _sentinel, fast_only = True):
        """ hashed get """
        hroot = self.root / hashroot
        hfile = hroot / gethashfile(key)

        d = self.get(hfile, _sentinel )
        #print "got dict",d,"from",hfile
        if d is _sentinel:
            if fast_only:
                if default is _sentinel:
                    raise KeyError(key)

                return default

            # slow mode ok, works even after hcompress()
            d = self.hdict(hashroot)

        return d.get(key, default)

    def hdict(self, hashroot):
        """ Get all data contained in hashed category 'hashroot' as dict """
        hfiles = self.keys(hashroot + "/*")
        hfiles.sort()
        last = len(hfiles) and hfiles[-1] or ''
        if last.endswith('xx'):
            # print "using xx"
            hfiles = [last] + hfiles[:-1]

        all = {}

        for f in hfiles:
            # print "using",f
            try:
                all.update(self[f])
            except KeyError:
                print("Corrupt",f,"deleted - hset is not threadsafe!")
                del self[f]

            self.uncache(f)

        return all

    def hcompress(self, hashroot):
        """ Compress category 'hashroot', so hset is fast again

        hget will fail if fast_only is True for compressed items (that were
        hset before hcompress).

        """
        hfiles = self.keys(hashroot + "/*")
        all = {}
        for f in hfiles:
            # print "using",f
            all.update(self[f])
            self.uncache(f)

        self[hashroot + '/xx'] = all
        for f in hfiles:
            p = self.root / f
            if p.name == 'xx':
                continue
            p.unlink()

    def __delitem__(self,key):
        """ del db["key"] """
        fil = self.root / key
        self.cache.pop(fil,None)
        try:
            fil.unlink()
        except OSError:
            # notfound and permission denied are ok - we
            # lost, the other process wins the conflict
            pass

    def _normalized(self, p):
        """ Make a key suitable for user's eyes """
        return str(p.relative_to(self.root)).replace('\\','/')

    def keys(self, globpat = None):
        """ All keys in DB, or all keys matching a glob"""

        if globpat is None:
            files = self.root.rglob('*')
        else:
            files = self.root.glob(globpat)
        return [self._normalized(p) for p in files if p.is_file()]

    def __iter__(self):
        return iter(self.keys())

    def __len__(self):
        return len(self.keys())

    def uncache(self,*items):
        """ Removes all, or specified items from cache

        Use this after reading a large amount of large objects
        to free up memory, when you won't be needing the objects
        for a while.

        """
        if not items:
            self.cache = {}
        for it in items:
            self.cache.pop(it,None)

    def waitget(self,key, maxwaittime = 60 ):
        """ Wait (poll) for a key to get a value

        Will wait for `maxwaittime` seconds before raising a KeyError.
        The call exits normally if the `key` field in db gets a value
        within the timeout period.

        Use this for synchronizing different processes or for ensuring
        that an unfortunately timed "db['key'] = newvalue" operation
        in another process (which causes all 'get' operation to cause a
        KeyError for the duration of pickling) won't screw up your program
        logic.
        """

        wtimes = [0.2] * 3 + [0.5] * 2 + [1]
        tries = 0
        waited = 0
        while 1:
            try:
                val = self[key]
                return val
            except KeyError:
                pass

            if waited > maxwaittime:
                raise KeyError(key)

            time.sleep(wtimes[tries])
            waited+=wtimes[tries]
            if tries < len(wtimes) -1:
                tries+=1

    def getlink(self,folder):
        """ Get a convenient link for accessing items  """
        return PickleShareLink(self, folder)

    def __repr__(self):
        return "PickleShareDB('%s')" % self.root

class PickleShareLink:
    """ A shortdand for accessing nested PickleShare data conveniently.

    Created through PickleShareDB.getlink(), example::

        lnk = db.getlink('myobjects/test')
        lnk.foo = 2
        lnk.bar = lnk.foo + 5

    """
    def __init__(self, db, keydir ):
        self.__dict__.update(locals())

    def __getattr__(self,key):
        return self.__dict__['db'][self.__dict__['keydir']+'/' + key]
    def __setattr__(self,key,val):
        self.db[self.keydir+'/' + key] = val
    def __repr__(self):
        db = self.__dict__['db']
        keys = db.keys( self.__dict__['keydir'] +"/*")
        return "<PickleShareLink '%s': %s>" % (
            self.__dict__['keydir'],
            ";".join([Path(k).basename() for k in keys]))

def main():
    import textwrap
    usage = textwrap.dedent("""\
    pickleshare - manage PickleShare databases

    Usage:

        pickleshare dump /path/to/db > dump.txt
        pickleshare load /path/to/db < dump.txt
        pickleshare test /path/to/db
    """)
    DB = PickleShareDB
    import sys
    if len(sys.argv) < 2:
        print(usage)
        return

    cmd = sys.argv[1]
    args = sys.argv[2:]
    if cmd == 'dump':
        if not args: args= ['.']
        db = DB(args[0])
        import pprint
        pprint.pprint(db.items())
    elif cmd == 'load':
        cont = sys.stdin.read()
        db = DB(args[0])
        data = eval(cont)
        db.clear()
        for k,v in db.items():
            db[k] = v
    elif cmd == 'testwait':
        db = DB(args[0])
        db.clear()
        print(db.waitget('250'))
    elif cmd == 'test':
        test()
        stress()

if __name__== "__main__":
    main()

<commit_msg>Don't convert Py2 unicode path to str<commit_after>#!/usr/bin/env python

""" PickleShare - a small 'shelve' like datastore with concurrency support

Like shelve, a PickleShareDB object acts like a normal dictionary. Unlike
shelve, many processes can access the database simultaneously. Changing a
value in database is immediately visible to other processes accessing the
same database.

Concurrency is possible because the values are stored in separate files. Hence
the "database" is a directory where *all* files are governed by PickleShare.

Example usage::

    from pickleshare import *
    db = PickleShareDB('~/testpickleshare')
    db.clear()
    print "Should be empty:",db.items()
    db['hello'] = 15
    db['aku ankka'] = [1,2,313]
    db['paths/are/ok/key'] = [1,(5,46)]
    print db.keys()
    del db['aku ankka']

This module is certainly not ZODB, but can be used for low-load
(non-mission-critical) situations where tiny code size trumps the
advanced features of a "real" object database.

Installation guide: pip install path pickleshare

Author: Ville Vainio <vivainio@gmail.com>
License: MIT open source license.

"""

from __future__ import print_function

__version__ = "0.7.3"

try:
    from pathlib import Path
except ImportError:
    # Python 2 backport
    from pathlib2 import Path

import os,stat,time
import collections
try:
    import cPickle as pickle
except ImportError:
    import pickle
import errno

def gethashfile(key):
    return ("%02x" % abs(hash(key) % 256))[-2:]

_sentinel = object()

class PickleShareDB(collections.MutableMapping):
    """ The main 'connection' object for PickleShare database """
    def __init__(self,root):
        """ Return a db object that will manage the specied directory"""
        if isinstance(root, Path):
            root = str(root)
        root = os.path.abspath(os.path.expanduser(root))
        self.root = Path(root)
        if not self.root.is_dir():
            # catching the exception is necessary if multiple processes are concurrently trying to create a folder
            # exists_ok keyword argument of mkdir does the same but only from Python 3.5
            try:
                self.root.mkdir(parents=True)
            except OSError as e:
                if e.errno != errno.EEXIST:
                    raise
        # cache has { 'key' : (obj, orig_mod_time) }
        self.cache = {}

    def __getitem__(self,key):
        """ db['key'] reading """
        fil = self.root / key
        try:
            mtime = (fil.stat()[stat.ST_MTIME])
        except OSError:
            raise KeyError(key)

        if fil in self.cache and mtime == self.cache[fil][1]:
            return self.cache[fil][0]
        try:
            # The cached item has expired, need to read
            with fil.open("rb") as f:
                obj = pickle.loads(f.read())
        except:
            raise KeyError(key)

        self.cache[fil] = (obj,mtime)
        return obj

    def __setitem__(self,key,value):
        """ db['key'] = 5 """
        fil = self.root / key
        parent = fil.parent
        if parent and not parent.is_dir():
            parent.mkdir(parents=True)
        # We specify protocol 2, so that we can mostly go between Python 2
        # and Python 3. We can upgrade to protocol 3 when Python 2 is obsolete.
        with fil.open('wb') as f:
            pickle.dump(value, f, protocol=2)
        try:
            self.cache[fil] = (value, fil.stat().st_mtime)
        except OSError as e:
            if e.errno != errno.ENOENT:
                raise

    def hset(self, hashroot, key, value):
        """ hashed set """
        hroot = self.root / hashroot
        if not hroot.is_dir():
            hroot.mkdir()
        hfile = hroot / gethashfile(key)
        d = self.get(hfile, {})
        d.update( {key : value})
        self[hfile] = d

    def hget(self, hashroot, key, default = _sentinel, fast_only = True):
        """ hashed get """
        hroot = self.root / hashroot
        hfile = hroot / gethashfile(key)

        d = self.get(hfile, _sentinel )
        #print "got dict",d,"from",hfile
        if d is _sentinel:
            if fast_only:
                if default is _sentinel:
                    raise KeyError(key)

                return default

            # slow mode ok, works even after hcompress()
            d = self.hdict(hashroot)

        return d.get(key, default)

    def hdict(self, hashroot):
        """ Get all data contained in hashed category 'hashroot' as dict """
        hfiles = self.keys(hashroot + "/*")
        hfiles.sort()
        last = len(hfiles) and hfiles[-1] or ''
        if last.endswith('xx'):
            # print "using xx"
            hfiles = [last] + hfiles[:-1]

        all = {}

        for f in hfiles:
            # print "using",f
            try:
                all.update(self[f])
            except KeyError:
                print("Corrupt",f,"deleted - hset is not threadsafe!")
                del self[f]

            self.uncache(f)

        return all

    def hcompress(self, hashroot):
        """ Compress category 'hashroot', so hset is fast again

        hget will fail if fast_only is True for compressed items (that were
        hset before hcompress).

        """
        hfiles = self.keys(hashroot + "/*")
        all = {}
        for f in hfiles:
            # print "using",f
            all.update(self[f])
            self.uncache(f)

        self[hashroot + '/xx'] = all
        for f in hfiles:
            p = self.root / f
            if p.name == 'xx':
                continue
            p.unlink()

    def __delitem__(self,key):
        """ del db["key"] """
        fil = self.root / key
        self.cache.pop(fil,None)
        try:
            fil.unlink()
        except OSError:
            # notfound and permission denied are ok - we
            # lost, the other process wins the conflict
            pass

    def _normalized(self, p):
        """ Make a key suitable for user's eyes """
        return str(p.relative_to(self.root)).replace('\\','/')

    def keys(self, globpat = None):
        """ All keys in DB, or all keys matching a glob"""

        if globpat is None:
            files = self.root.rglob('*')
        else:
            files = self.root.glob(globpat)
        return [self._normalized(p) for p in files if p.is_file()]

    def __iter__(self):
        return iter(self.keys())

    def __len__(self):
        return len(self.keys())

    def uncache(self,*items):
        """ Removes all, or specified items from cache

        Use this after reading a large amount of large objects
        to free up memory, when you won't be needing the objects
        for a while.

        """
        if not items:
            self.cache = {}
        for it in items:
            self.cache.pop(it,None)

    def waitget(self,key, maxwaittime = 60 ):
        """ Wait (poll) for a key to get a value

        Will wait for `maxwaittime` seconds before raising a KeyError.
        The call exits normally if the `key` field in db gets a value
        within the timeout period.

        Use this for synchronizing different processes or for ensuring
        that an unfortunately timed "db['key'] = newvalue" operation
        in another process (which causes all 'get' operation to cause a
        KeyError for the duration of pickling) won't screw up your program
        logic.
        """

        wtimes = [0.2] * 3 + [0.5] * 2 + [1]
        tries = 0
        waited = 0
        while 1:
            try:
                val = self[key]
                return val
            except KeyError:
                pass

            if waited > maxwaittime:
                raise KeyError(key)

            time.sleep(wtimes[tries])
            waited+=wtimes[tries]
            if tries < len(wtimes) -1:
                tries+=1

    def getlink(self,folder):
        """ Get a convenient link for accessing items  """
        return PickleShareLink(self, folder)

    def __repr__(self):
        return "PickleShareDB('%s')" % self.root

class PickleShareLink:
    """ A shortdand for accessing nested PickleShare data conveniently.

    Created through PickleShareDB.getlink(), example::

        lnk = db.getlink('myobjects/test')
        lnk.foo = 2
        lnk.bar = lnk.foo + 5

    """
    def __init__(self, db, keydir ):
        self.__dict__.update(locals())

    def __getattr__(self,key):
        return self.__dict__['db'][self.__dict__['keydir']+'/' + key]
    def __setattr__(self,key,val):
        self.db[self.keydir+'/' + key] = val
    def __repr__(self):
        db = self.__dict__['db']
        keys = db.keys( self.__dict__['keydir'] +"/*")
        return "<PickleShareLink '%s': %s>" % (
            self.__dict__['keydir'],
            ";".join([Path(k).basename() for k in keys]))

def main():
    import textwrap
    usage = textwrap.dedent("""\
    pickleshare - manage PickleShare databases

    Usage:

        pickleshare dump /path/to/db > dump.txt
        pickleshare load /path/to/db < dump.txt
        pickleshare test /path/to/db
    """)
    DB = PickleShareDB
    import sys
    if len(sys.argv) < 2:
        print(usage)
        return

    cmd = sys.argv[1]
    args = sys.argv[2:]
    if cmd == 'dump':
        if not args: args= ['.']
        db = DB(args[0])
        import pprint
        pprint.pprint(db.items())
    elif cmd == 'load':
        cont = sys.stdin.read()
        db = DB(args[0])
        data = eval(cont)
        db.clear()
        for k,v in db.items():
            db[k] = v
    elif cmd == 'testwait':
        db = DB(args[0])
        db.clear()
        print(db.waitget('250'))
    elif cmd == 'test':
        test()
        stress()

if __name__== "__main__":
    main()

<|endoftext|>


<commit_before>from configparser import ConfigParser
config = ConfigParser()
config.read('config.cfg')

twilio_account = '234323432'#config['Twilio']['account']
twilio_token = '234323432'#config['Twilio']['token']
twilio_sender_number = '234323432'#config['Twilio']['sender_number']
twilio_recipient_number = '234323432'#config['Twilio']['recipient_number']

sql_alchemy_connection_string = 'sqlite:///piot.db'<commit_msg>Backed Out Stale Config<commit_after>from configparser import ConfigParser
config = ConfigParser()
config.read('config.cfg')

twilio_account = config['Twilio']['account']
twilio_token = config['Twilio']['token']
twilio_sender_number = config['Twilio']['sender_number']
twilio_recipient_number = config['Twilio']['recipient_number']

sql_alchemy_connection_string = 'sqlite:///piot.db'
<|endoftext|>


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

#####################################################################################
#
#    Pipe something to Pidgin.
#    Copyright (C) 2013  Max Rosin  git@hackrid.de
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#####################################################################################

import dbus
import sys

if len(sys.argv) != 2:
    print("Usage example: echo 'Hello World' | ./pipe2pidgin.py 'yourpidgincontact@example.com'")
    sys.exit(-1)

contact = sys.argv[1]
PURPLE_CONV_TYPE_IM = 1

message = sys.stdin.read()

bus = dbus.SessionBus()
obj = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")

accounts = purple.PurpleAccountsGetAllActive()

for account in accounts:
    buddy = purple.PurpleFindBuddy(account, contact)
    if buddy != 0 and purple.PurpleBuddyIsOnline(buddy):
        conversation = purple.PurpleConversationNew(PURPLE_CONV_TYPE_IM, account, contact)
        purple.PurpleConvImSend(purple.PurpleConvIm(conversation), message)
<commit_msg>removed whitespace from shebang<commit_after>#!/usr/bin/env python

#####################################################################################
#
#    Pipe something to Pidgin.
#    Copyright (C) 2013  Max Rosin  git@hackrid.de
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#####################################################################################

import dbus
import sys

if len(sys.argv) != 2:
    print("Usage example: echo 'Hello World' | ./pipe2pidgin.py 'yourpidgincontact@example.com'")
    sys.exit(-1)

contact = sys.argv[1]
PURPLE_CONV_TYPE_IM = 1

message = sys.stdin.read()

bus = dbus.SessionBus()
obj = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")

accounts = purple.PurpleAccountsGetAllActive()

for account in accounts:
    buddy = purple.PurpleFindBuddy(account, contact)
    if buddy != 0 and purple.PurpleBuddyIsOnline(buddy):
        conversation = purple.PurpleConversationNew(PURPLE_CONV_TYPE_IM, account, contact)
        purple.PurpleConvImSend(purple.PurpleConvIm(conversation), message)
<|endoftext|>


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

#####################################################################################
#
#    Pipe something to Pidgin.
#    Copyright (C) 2013  Max Rosin  git@hackrid.de
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#####################################################################################

import dbus
import sys

if len(sys.argv) != 2:
    print("Usage example: echo 'Hello World' | ./pipe2pidgin.py 'yourpidgincontact@example.com'")
    sys.exit(-1)

contact = sys.argv[1]
PURPLE_CONV_TYPE_IM = 1

message = sys.stdin.read()

bus = dbus.SessionBus()
obj = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")

accounts = purple.PurpleAccountsGetAllActive()

for account in accounts:
    buddy = purple.PurpleFindBuddy(account, contact)
    if buddy != 0 and purple.PurpleBuddyIsOnline(buddy):
        conversation = purple.PurpleConversationNew(PURPLE_CONV_TYPE_IM, account, contact)
        purple.PurpleConvImSend(purple.PurpleConvIm(conversation), message)
<commit_msg>added new usage instruction<commit_after>#!/usr/bin/env python

#####################################################################################
#
#    Pipe something to Pidgin.
#    Copyright (C) 2013  Max Rosin  git@hackrid.de
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#####################################################################################

import dbus
import sys

if len(sys.argv) != 2:
    print("Usage example:")
    print(" echo 'Hello World' | ./pipe2pidgin.py 'yourpidgincontact@example.com'")
    print(" ./pipe2pidgin.py filename 'yourpidgincontact@example.com'")
    sys.exit(-1)

contact = sys.argv[1]
PURPLE_CONV_TYPE_IM = 1

message = sys.stdin.read()

bus = dbus.SessionBus()
obj = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")

accounts = purple.PurpleAccountsGetAllActive()

for account in accounts:
    buddy = purple.PurpleFindBuddy(account, contact)
    if buddy != 0 and purple.PurpleBuddyIsOnline(buddy):
        conversation = purple.PurpleConversationNew(PURPLE_CONV_TYPE_IM, account, contact)
        purple.PurpleConvImSend(purple.PurpleConvIm(conversation), message)
<|endoftext|>


<commit_before>class DotDict(object):
    """
    Immutable dict-like objects accessible by dot notation

    Used because the amount of configuration access is very high and just using
    dots instead of the dict notation feels good.

    """

    def __init__(self, data):
        self.data = data

    def __repr__(self):  # pragma: nocover
        return '<DotDict {}>'.format(self.data)

    def __getattr__(self, key):
        val = self.data[key]

        if isinstance(val, dict):
            val = DotDict(val)

        return val

    def values(self):
        return self.data.values()

    def __eq__(self, other):
        return self.data == other.data

    # So that we can still access as dicts
    __getitem__ = __getattr__

def dynamic_load(target):
    """
    Dynamically import a class and return it

    This is used by the core parts of the main configuration file since
    one of the main features is to let the user specify which class to use.

    """

    split = target.split('.')
    module_name = '.'.join(split[:-1])
    class_name = split[-1]

    mod = __import__(module_name, fromlist=[class_name])
    return getattr(mod, class_name)
<commit_msg>Make DotDict dict methods return those objects<commit_after>class DotDict(object):
    """
    Immutable dict-like objects accessible by dot notation

    Used because the amount of configuration access is very high and just using
    dots instead of the dict notation feels good.

    """

    def __init__(self, data):
        self.data = data

    def __repr__(self):  # pragma: nocover
        return '<DotDict {}>'.format(self.data)

    def __getattr__(self, key):
        if key in ('values', 'keys', 'items'):
            # Dict methods, just return and run them.
            return getattr(self.data, key)

        val = self.data[key]

        if isinstance(val, dict):
            val = DotDict(val)

        return val

    def __eq__(self, other):
        return self.data == other.data

    # So that we can still access as dicts
    __getitem__ = __getattr__

def dynamic_load(target):
    """
    Dynamically import a class and return it

    This is used by the core parts of the main configuration file since
    one of the main features is to let the user specify which class to use.

    """

    split = target.split('.')
    module_name = '.'.join(split[:-1])
    class_name = split[-1]

    mod = __import__(module_name, fromlist=[class_name])
    return getattr(mod, class_name)
<|endoftext|>


<commit_before>from flask import Flask, jsonify, json, abort, request
from datetime import datetime, timedelta
from os import getloadavg
import scraper
import os
import configparser
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

if os.getenv("env") is not "development":
    try:
        config = configparser.ConfigParser()
        config.read("config.ini")
        server_host = config["Server"]["host"]
        server_port = int(config["Server"]["port"])
        server_mail = config["Server"]["mail"]
    except (KeyError, ValueError):
        server_host = "localhost"
        server_port = 5000
        server_mail = ""

supported_cities = []

@app.route("/")
def get_meta():
    return jsonify({
        "mail": server_mail,
        "cities": supported_cities
    })

@app.route("/status")
def get_api_status():
    return jsonify({
        "status": "online",
        "servertime": datetime.now(),
        "load": getloadavg()
    })

@app.route("/<city>")
def get_lots(city):

    app.logger.info("GET /" + city + " - " + request.headers.get("User-Agent"))

    if city not in supported_cities:
        app.logger.info("Unsupported city: " + city)
        return jsonify({
            "error": "Sorry, '" + city + "' isn't supported at the current time."
        })
    try:
        file = open("./cache/" + city + ".json", "r")
        last_json = file.read()
        last_json = json.loads(last_json)
        last_downloaded = datetime.strptime(last_json["last_downloaded"], "%Y-%m-%d %H:%M:%S")
        if datetime.now() - last_downloaded <= timedelta(minutes=10):
            app.logger.debug("Using cached data")
            return jsonify(last_json)
        else:
            return jsonify(scraper.live(city))
    except FileNotFoundError:
        return jsonify(scraper.live(city))

# @app.route("/<city>/<lot_id>")
# def get_lot_details(city, lot_id):
#     if city == "Dresden":
#         return Dresden.get_lot_details(lot_id)

@app.route("/coffee")
def make_coffee():
    abort(418)

def gather_supported_cities():
    """
    Iterate over files in ./cities to add them to list of available cities.
    This list is used to stop requests trying to access files and output them which are not cities.
    """
    for file in os.listdir(os.curdir + "/cities"):
        if file.endswith(".py") and "__Init__" not in file.title() and "Sample_City" not in file.title():
            supported_cities.append(file[:-3])

if __name__ == "__main__":
    log_handler = RotatingFileHandler("server.log", maxBytes=10000, backupCount=1)

    if os.getenv("env") == "development":
        log_handler.setLevel(logging.DEBUG)
        app.logger.addHandler(log_handler)

        gather_supported_cities()
        app.run(debug=True)
    else:
        log_handler.setLevel(logging.INFO)
        app.logger.addHandler(log_handler)

        gather_supported_cities()

        app.run(host=server_host, port=server_port)
<commit_msg>Adjust loglevels<commit_after>from flask import Flask, jsonify, json, abort, request
from datetime import datetime, timedelta
from os import getloadavg
import scraper
import os
import configparser
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

if os.getenv("env") is not "development":
    try:
        config = configparser.ConfigParser()
        config.read("config.ini")
        server_host = config["Server"]["host"]
        server_port = int(config["Server"]["port"])
        server_mail = config["Server"]["mail"]
    except (KeyError, ValueError):
        server_host = "localhost"
        server_port = 5000
        server_mail = ""

supported_cities = []

@app.route("/")
def get_meta():
    return jsonify({
        "mail": server_mail,
        "cities": supported_cities
    })

@app.route("/status")
def get_api_status():
    return jsonify({
        "status": "online",
        "servertime": datetime.now(),
        "load": getloadavg()
    })

@app.route("/<city>")
def get_lots(city):

    app.logger.info("GET /" + city + " - " + request.headers.get("User-Agent"))

    if city not in supported_cities:
        app.logger.info("Unsupported city: " + city)
        return jsonify({
            "error": "Sorry, '" + city + "' isn't supported at the current time."
        })
    try:
        file = open("./cache/" + city + ".json", "r")
        last_json = file.read()
        last_json = json.loads(last_json)
        last_downloaded = datetime.strptime(last_json["last_downloaded"], "%Y-%m-%d %H:%M:%S")
        if datetime.now() - last_downloaded <= timedelta(minutes=10):
            app.logger.debug("Using cached data")
            return jsonify(last_json)
        else:
            return jsonify(scraper.live(city))
    except FileNotFoundError:
        return jsonify(scraper.live(city))

# @app.route("/<city>/<lot_id>")
# def get_lot_details(city, lot_id):
#     if city == "Dresden":
#         return Dresden.get_lot_details(lot_id)

@app.route("/coffee")
def make_coffee():
    abort(418)

def gather_supported_cities():
    """
    Iterate over files in ./cities to add them to list of available cities.
    This list is used to stop requests trying to access files and output them which are not cities.
    """
    for file in os.listdir(os.curdir + "/cities"):
        if file.endswith(".py") and "__Init__" not in file.title() and "Sample_City" not in file.title():
            supported_cities.append(file[:-3])

if __name__ == "__main__":
    log_handler = RotatingFileHandler("server.log", maxBytes=10000, backupCount=1)

    if os.getenv("env") == "development":
        app.logger.addHandler(log_handler)

        gather_supported_cities()
        app.run(debug=True)
    else:
        app.logger.setLevel(logging.INFO)
        log_handler.setLevel(logging.INFO)
        app.logger.addHandler(log_handler)

        gather_supported_cities()

        app.run(host=server_host, port=server_port)
<|endoftext|>


<commit_before>import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('0.0.0.0', 8000))
sock.listen(1)
while True:
    conn, addr = sock.accept()
    print('connected by', addr)
    while True:
        data = conn.recv(1024)
        if not data: break
        conn.sendall(data)
    conn.close()
sock.close()
<commit_msg>Http socket server<commit_after>import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('0.0.0.0', 8000))
sock.listen(1)
while True:
    conn, addr = sock.accept()
    print('connected by', addr)

    data = conn.recv(1024)
    print('received', data)

    response = b'''\
HTTP/1.1 200 OK

Hello World!
'''
    conn.sendall(response)
    conn.close()
sock.close()
<|endoftext|>


<commit_before>#!/usr/bin/env python3
from two1.lib.wallet import Wallet
from two1.lib.bitserv.flask import Payment
from flask import Flask, jsonify

app = Flask(__name__)
wallet = Wallet()
payment = Payment(app, wallet)

def bad_request(message):
    response = jsonify({'message': message})
    response.status_code = 400
    return response

def get_price(request):
    r = request.post(url='http://localhost:3000/validateAndPrice', json=request.data)

    if r.result.Status == 1 || r.result.Status == 0:
        price_in_usd = r.result.Order.Amounts.Payment

        get_bitpay_btc_usd_rate = request.urlopen(url="https://bitpay.com/api/rates/usd").read().decode("utf-8")
        usd_per_btc = json.loads(get_bitpay_btc_usd_rate)["rate"]

        price = price_in_usd * 10^8 / usd_per_btc
    else:
        setattr(request, 'error_validate', 'error_validate')
        price = 0

    return price

@app.route('/order', methods=['POST'])
@payment.required(get_price)
def order():
    if hasattr(request, 'error_validate'):
        return bad_request('There is a problem with your order details.')

    return 'Pizza ordered!'

@app.route('/validate', methods=['POST'])
def validate():
    price = get_price(request)
    return price

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
<commit_msg>server<->order<commit_after>#!/usr/bin/env python3
from two1.lib.wallet import Wallet
from two1.lib.bitserv.flask import Payment
from flask import Flask, jsonify

app = Flask(__name__)
wallet = Wallet()
payment = Payment(app, wallet)

def bad_request(message):
    response = jsonify({'message': message})
    response.status_code = 400
    return response

def get_price(request):
    r = request.post(url='http://localhost:3000/validateAndPrice', json=request.data)
    response_data = json.loads(r.read().decode("utf-8"))

    if r.result.Status == 1 | r.result.Status == 0:
        price_in_usd = r.result.Order.Amounts.Payment

        get_bitpay_btc_usd_rate = request.urlopen(url="https://bitpay.com/api/rates/usd").read().decode("utf-8")
        usd_per_btc = json.loads(get_bitpay_btc_usd_rate)["rate"]

        price = price_in_usd * 10^8 / usd_per_btc
    else:
        setattr(request, 'error_validate', 'error_validate')
        price = 0

    return price

@app.route('/order', methods=['POST'])
@payment.required(get_price)
def order():
    if hasattr(request, 'error_validate'):
        return bad_request('There is a problem with your order details.')

    return 'Pizza ordered!'

@app.route('/validate', methods=['POST'])
def validate():
    price = get_price(request)
    return price

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
<|endoftext|>