<commit_before># Module for graph drawing and maintenance
from gi.repository import GooCanvas
from gi.repository import Gdk
import networkx as nx
from textwrap import TextWrapper
from numpy import mean
from math import sqrt

import Errors
import painters

# Custom canvas class to handle graph drawing and interaction
class Canvas(GooCanvas.Canvas):
    '''Custom GooCanvas that natively handles node/edge drawing with networkx.'''

    def __init__(self, **args):
        '''Create a Canvas object. **args are passed to GooCanvas.Canvas constructor.'''
        GooCanvas.Canvas.__init__(self, **args)
        self.set_properties(automatic_bounds=True,
                            integer_layout=False,
                            bounds_from_origin=False,
                            bounds_padding=10)
        self.root = self.get_root_item()
        self.root.set_properties(fill_color='white')

        #initialize internal vars
        self.zoom = False
        self.node_callback = None
        self.line_callback = None
        self.key_handler = None
        self.cboxes = []
        self.textwrap = TextWrapper(width=8) #text wrapper for node labels
        self.space = None

        self.gbox = GooCanvas.CanvasGroup(parent = self.root)

    def redraw(self, G):
        '''Draw the networkx graph G, including new layout.'''
        #first we clear off the old drawing
        for c in self.cboxes:
            c.remove()

        del self.cboxes[:]

        #get locations from the graph
        components = nx.connected_component_subgraphs(G)
        for subg in components:
            locations = nx.spring_layout(subg, scale=150*subg.order())
            cbox = SubGraph(parent = self.gbox, locs=locations, graph=subg)
            self.cboxes.append(cbox)

            #iterate over the nodes and draw each according to its given positions
            for gnode in subg.nodes_iter():
                nodeobj = G.node[gnode]['node']
                pos = locations[gnode]
                lbl_text = self.textwrap.fill(gnode)

                #initialize background ring for spacing
                #done before the vertex so it'll be in the background and not interrupt clicking
                ring = GooCanvas.CanvasEllipse(parent=cbox, fill_color_rgba=0x00000000, stroke_color_rgba=0x00000000)

                #TODO assign style info to object based on style rules
                #   change painter if necessary
                ngroup = Vertex(nodeobj, parent=cbox, x=pos[0], y=pos[1], painter=painters.vertex.box, text=lbl_text)
                ngroup.connect("button-press-event", self.node_callback)
                cbox.vertices[ngroup.label] = ngroup
                cbox.spacers[ngroup.label] = ring

                #define ring properties
                coords = ngroup.get_xyr()
                ring.set_properties(radius_x=coords['radius'], radius_y=coords['radius'], center_x=coords['x'], center_y=coords['y'])

            #iterate through edges and draw each according to its stored relationships
            for snode, enode, props in subg.edges_iter(data=True):
                #TODO assign style info to object based on style rules
                #   change painter if necessary
                line = AggLine(parent=cbox, fnode=cbox.vertices[snode], tnode=cbox.vertices[enode], rels=props['rels'], painter=painters.edge.line)
                cbox.edges.append(line)

                #TODO attach this callback to individual relationships, not just the aggline
                #line.connect("button-press-event", self.line_callback)

        self.pack()

    def freshen(self):
        '''Update visuals without calculating a new layout.'''
        for sub in self.cboxes:
            sub.update()

    def pack(self):
        '''Pack component subgraphs into the drawing space.'''
        if len(self.cboxes) == 0:
            return

        del self.space

        #figure out the maximum possible dimensions by layout all our subgraphs end-to-end
        worst_case = 0
        boxes = []
        for subg in self.cboxes:
            bounds = subg.get_bounds()
            h = bounds.y2 - bounds.y1
            w = bounds.x2 - bounds.x1
            boxes.append((w*h, subg))
            worst_case += max(h, w)
        keys = sorted(boxes, reverse=True)

        #TODO using the worst case number for both width and height results in
        #the packer putting all items on either the X or Y axis, whichever it picks
        #first. The width and height need to be tightened, but I don't know how, yet.
        self.space = Packer(0, 0, worst_case, worst_case)
        for key, subg in keys:
            bounds = subg.get_bounds()
            h = bounds.y2 - bounds.y1
            w = bounds.x2 - bounds.x1
            (x, y) = self.space.place(w, h)
            subg.set_properties(x=x, y=y)

    def get_vertex(self, label):
        '''Find vertex object by label.'''
        for subg in self.cboxes:
            if label in subg.vertices:
                return subg.vertices[label]

        return None

    def get_agglines(self, label):
        '''Find all agglines which touch node label.'''
        pass

class Packer():
    '''Tree for packing rectangles into an area.'''
    def __init__(self, x, y, width, height):
        '''Set up this node of the tree.'''
        self.x = x
        self.y = y
        self.w = width
        self.h = height
        self.child = None
        self.used = False

    def place(self, width, height):
        '''Find coordinates for a box of width,height.'''
        if self.child != None:
            #if we aren't a child, try our 0 child first
            coords = self.child[0].place(width, height)
            if coords != None:
                return coords

            #if that fails, try the 1 child
            return self.child[1].place(width, height)
        else:
            #if we're a leaf...
            #can't place something if we're occupied
            if self.used: return None
            #nor if the box is too big for us
            if width > self.w or height > self.h: return None
            #but if it's exactly the right size, go ahead
            if width == self.w and height == self.h:
                self.used = True
                return (self.x, self.y)

            #by now, we have the space to hold said box, so let's allocate it
            self.child = []

            dw = self.w - width
            dh = self.h - height

            if dw > dh:
                #set up children so child[0] has the exact width needed
                self.child.append(Packer(self.x, self.y, width, self.h))
                self.child.append(Packer(self.x + width, self.y, self.w - width, self.h))
            else:
                #set up children so child[0] has the exact height needed
                self.child.append(Packer(self.x, self.y, self.w, height))
                self.child.append(Packer(self.x, self.y + height, self.w, self.h - height))

            #ask our tailored child to place the box
            return self.child[0].place(width, height)

class AggLine(GooCanvas.CanvasGroup):
    '''Represent an aggregate line with properties derived from all the relationships between its start and end points.'''

    def __init__(self, fnode, tnode, rels=None, painter=None, **args):
        '''Create a new aggregate line.'''
        GooCanvas.CanvasGroup.__init__(self, **args)

        self.type = 'rel'
        self.start_arrow = False
        self.end_arrow = False
        self.width = 5
        self.weights = []
        self.origin = fnode #Node object
        self.dest = tnode #Node object
        self.labels = [] #list of tuples (label, dir) where dir is 'from', 'to', or 'both'
        self.painter = painter
        self.selected = False

        #parse relationships
        if rels != None:
            for rel in rels:
                self.add_rel(rel)

        #draw if we're able
        if painter != None: self.draw()

    def add_rel(self, rel):
        '''Add properties from a relationship object.'''

        #add labels and arrows according to directionality
        if rel.mutual:
            self.labels.append((rel.label, 'both'))
            if not (self.start_arrow and self.end_arrow):
                self.start_arrow = True
                self.end_arrow = True
        if rel.ends_at(self.origin.label):
            self.labels.append((rel.label, 'from'))
            if not self.start_arrow:
                self.start_arrow = True
        if rel.ends_at(self.dest.label):
            self.labels.append((rel.label, 'to'))
            if not self.end_arrow:
                self.end_arrow = True

        #add weight to width concern
        self.weights.append(rel.weight)
        self.calc_width()

    def calc_width(self):
        '''Calculate line width from relationship weights.'''
        self.width = mean(self.weights)

    def set_painter(self, painter):
        '''Set the painting function used to draw this vertex, then call it.'''
        self.painter = painter

    def draw(self):
        '''Draw with our painter.'''
        if self.painter == None:
            return

        #remove any child objects we have
        if self.get_n_children():
            self.get_child(0).remove()

        shape = self.painter.paint(parent=self, start=self.origin.get_xyr(), end=self.dest.get_xyr(), lobj=self)

    def set_selected(self, state):
        '''Mark our selected status and draw selection ring.'''
        self.selected = state
        self.painter.show_selected(state)

    def clear_rels(self):
        '''Clear out all relationship-derived data.'''
        del self.weights[:]
        del self.labels[:]

class Vertex(GooCanvas.CanvasGroup):
    '''Represent a node on the canvas.'''

    def __init__(self, node, x=0, y=0, painter=None, text=None, **args):
        '''Create a new Vertex which represents node.'''
        GooCanvas.CanvasGroup.__init__(self, **args)

        self.type = 'node'
        self.node = node
        self.label = node.label
        self.x = x
        self.y = y
        self.radius = 0
        self.painter = painter
        self.selected = False
        self.selring = None
        self.text = text

        if text == None:
            self.text = self.label

        #if we were initialized with a painting function, draw immediately
        if painter != None: self.draw()

    def set_painter(self, painter):
        '''Set the painting function used to draw this object.'''
        self.painter = painter

    def draw(self):
        '''Draw with our painter, then recalculate x, y, and radius.'''
        if self.painter == None:
            return

        #remove any child objects we have
        if self.get_n_children():
            self.get_child(0).remove()

        #draw some new ones
        shape = self.painter.paint(parent=self, node=self.node)
        self.width = shape['width']
        self.height = shape['height']

        #now that something's drawn, center ourselves around our original x,y
        self.set_properties(x = self.x - self.width/2, y = self.y - self.height/2)

        #calculate and store our new radius
        bounds = self.get_bounds()
        dx = bounds.x1 - self.x
        dy = bounds.y1 - self.y
        self.radius = sqrt(dx*dx + dy*dy)

    def get_xyr(self):
        '''Return a dict of x, y, and radius.'''
        return {'x':self.x, 'y':self.y, 'radius':self.radius}

    def set_selected(self, state):
        '''Mark our selected status and draw selection ring.'''
        self.selected = state

        if self.selected:
            self.selring = self.painter.show_selected(self)
        else:
            self.selring.remove()

class SubGraph(GooCanvas.CanvasGroup):
    '''Represents a connected subgraph on the graph.'''

    def __init__(self, locs=None, graph=None, **args):
        '''Set up accounting structures and init canvasgroup.'''
        GooCanvas.CanvasGroup.__init__(self, **args)

        self.locations = locs
        self.G = graph
        self.vertices = {}
        self.spacers = {} #dict of spacing rings for each vertex
        self.edges = []

    def update(self):
        '''Redraw the subgroup without recalculating anything.'''
        for k, v in self.vertices.iteritems():
            v.draw()

            #update spacer
            coords = v.get_xyr()
            self.spacers[v.label].set_properties(radius_x=coords['radius'], radius_y=coords['radius'], center_x=coords['x'], center_y=coords['y'])

        for line in self.edges:
            line.clear_rels()
            #populate new rels from stored origin/dest and self.G
            for rel in self.G[line.origin][line.dest]:
                line.add_rel(rel)
            line.draw()
<commit_msg>changed packing bounds to be sane; fixes #2<commit_after># Module for graph drawing and maintenance
from gi.repository import GooCanvas
from gi.repository import Gdk
import networkx as nx
from textwrap import TextWrapper
from numpy import mean
from math import sqrt

import Errors
import painters

# Custom canvas class to handle graph drawing and interaction
class Canvas(GooCanvas.Canvas):
    '''Custom GooCanvas that natively handles node/edge drawing with networkx.'''

    def __init__(self, **args):
        '''Create a Canvas object. **args are passed to GooCanvas.Canvas constructor.'''
        GooCanvas.Canvas.__init__(self, **args)
        self.set_properties(automatic_bounds=True,
                            integer_layout=False,
                            bounds_from_origin=False,
                            bounds_padding=10)
        self.root = self.get_root_item()
        self.root.set_properties(fill_color='white')

        #initialize internal vars
        self.zoom = False
        self.node_callback = None
        self.line_callback = None
        self.key_handler = None
        self.cboxes = []
        self.textwrap = TextWrapper(width=8) #text wrapper for node labels
        self.space = None

        self.gbox = GooCanvas.CanvasGroup(parent = self.root)

    def redraw(self, G):
        '''Draw the networkx graph G, including new layout.'''
        #first we clear off the old drawing
        for c in self.cboxes:
            c.remove()

        del self.cboxes[:]

        #get locations from the graph
        components = nx.connected_component_subgraphs(G)
        for subg in components:
            locations = nx.spring_layout(subg, scale=150*subg.order())
            cbox = SubGraph(parent = self.gbox, locs=locations, graph=subg)
            self.cboxes.append(cbox)

            #iterate over the nodes and draw each according to its given positions
            for gnode in subg.nodes_iter():
                nodeobj = G.node[gnode]['node']
                pos = locations[gnode]
                lbl_text = self.textwrap.fill(gnode)

                #initialize background ring for spacing
                #done before the vertex so it'll be in the background and not interrupt clicking
                ring = GooCanvas.CanvasEllipse(parent=cbox, fill_color_rgba=0x00000000, stroke_color_rgba=0x00000000)

                #TODO assign style info to object based on style rules
                #   change painter if necessary
                ngroup = Vertex(nodeobj, parent=cbox, x=pos[0], y=pos[1], painter=painters.vertex.box, text=lbl_text)
                ngroup.connect("button-press-event", self.node_callback)
                cbox.vertices[ngroup.label] = ngroup
                cbox.spacers[ngroup.label] = ring

                #define ring properties
                coords = ngroup.get_xyr()
                ring.set_properties(radius_x=coords['radius'], radius_y=coords['radius'], center_x=coords['x'], center_y=coords['y'])

            #iterate through edges and draw each according to its stored relationships
            for snode, enode, props in subg.edges_iter(data=True):
                #TODO assign style info to object based on style rules
                #   change painter if necessary
                line = AggLine(parent=cbox, fnode=cbox.vertices[snode], tnode=cbox.vertices[enode], rels=props['rels'], painter=painters.edge.line)
                cbox.edges.append(line)

                #TODO attach this callback to individual relationships, not just the aggline
                #line.connect("button-press-event", self.line_callback)

        self.pack()

    def freshen(self):
        '''Update visuals without calculating a new layout.'''
        for sub in self.cboxes:
            sub.update()

    def pack(self):
        '''Pack component subgraphs into the drawing space.'''
        if len(self.cboxes) == 0:
            return

        del self.space

        #figure out the maximum possible dimensions by layout all our subgraphs end-to-end
        tog = 0
        worst_w = 0
        worst_h = 0
        boxes = []
        for subg in self.cboxes:
            bounds = subg.get_bounds()
            h = bounds.y2 - bounds.y1
            w = bounds.x2 - bounds.x1
            boxes.append((w*h, subg, w, h))

            #add each box to either the width or height of the max bounds
            #this ensures relatively sane packing behavior
            worst_w = max(worst_w, w)
            worst_h = max(worst_h, h)
            if tog:
                worst_w += w
            else:
                worst_h += h
            tog = not tog

        #sort the subgraphs by size
        keys = sorted(boxes, reverse=True)

        #pack each individually
        self.space = Packer(0, 0, worst_w, worst_h)
        for key, subg, w, h in keys:
            (x, y) = self.space.place(w, h)
            subg.set_properties(x=x, y=y)

    def get_vertex(self, label):
        '''Find vertex object by label.'''
        for subg in self.cboxes:
            if label in subg.vertices:
                return subg.vertices[label]

        return None

    def get_agglines(self, label):
        '''Find all agglines which touch node label.'''
        pass

class Packer():
    '''Tree for packing rectangles into an area.'''
    def __init__(self, x, y, width, height):
        '''Set up this node of the tree.'''
        self.x = x
        self.y = y
        self.w = width
        self.h = height
        self.child = None
        self.used = False

    def place(self, width, height):
        '''Find coordinates for a box of width,height.'''
        if self.child != None:
            #if we aren't a child, try our 0 child first
            coords = self.child[0].place(width, height)
            if coords != None:
                return coords

            #if that fails, try the 1 child
            return self.child[1].place(width, height)
        else:
            #if we're a leaf...
            #can't place something if we're occupied
            if self.used: return None
            #nor if the box is too big for us
            if width > self.w or height > self.h: return None
            #but if it's exactly the right size, go ahead
            if width == self.w and height == self.h:
                self.used = True
                return (self.x, self.y)

            #by now, we have the space to hold said box, so let's allocate it
            self.child = []

            dw = self.w - width
            dh = self.h - height

            if dw > dh:
                #set up children so child[0] has the exact width needed
                self.child.append(Packer(self.x, self.y, width, self.h))
                self.child.append(Packer(self.x + width, self.y, self.w - width, self.h))
            else:
                #set up children so child[0] has the exact height needed
                self.child.append(Packer(self.x, self.y, self.w, height))
                self.child.append(Packer(self.x, self.y + height, self.w, self.h - height))

            #ask our tailored child to place the box
            return self.child[0].place(width, height)

class AggLine(GooCanvas.CanvasGroup):
    '''Represent an aggregate line with properties derived from all the relationships between its start and end points.'''

    def __init__(self, fnode, tnode, rels=None, painter=None, **args):
        '''Create a new aggregate line.'''
        GooCanvas.CanvasGroup.__init__(self, **args)

        self.type = 'rel'
        self.start_arrow = False
        self.end_arrow = False
        self.width = 5
        self.weights = []
        self.origin = fnode #Node object
        self.dest = tnode #Node object
        self.labels = [] #list of tuples (label, dir) where dir is 'from', 'to', or 'both'
        self.painter = painter
        self.selected = False

        #parse relationships
        if rels != None:
            for rel in rels:
                self.add_rel(rel)

        #draw if we're able
        if painter != None: self.draw()

    def add_rel(self, rel):
        '''Add properties from a relationship object.'''

        #add labels and arrows according to directionality
        if rel.mutual:
            self.labels.append((rel.label, 'both'))
            if not (self.start_arrow and self.end_arrow):
                self.start_arrow = True
                self.end_arrow = True
        if rel.ends_at(self.origin.label):
            self.labels.append((rel.label, 'from'))
            if not self.start_arrow:
                self.start_arrow = True
        if rel.ends_at(self.dest.label):
            self.labels.append((rel.label, 'to'))
            if not self.end_arrow:
                self.end_arrow = True

        #add weight to width concern
        self.weights.append(rel.weight)
        self.calc_width()

    def calc_width(self):
        '''Calculate line width from relationship weights.'''
        self.width = mean(self.weights)

    def set_painter(self, painter):
        '''Set the painting function used to draw this vertex, then call it.'''
        self.painter = painter

    def draw(self):
        '''Draw with our painter.'''
        if self.painter == None:
            return

        #remove any child objects we have
        if self.get_n_children():
            self.get_child(0).remove()

        shape = self.painter.paint(parent=self, start=self.origin.get_xyr(), end=self.dest.get_xyr(), lobj=self)

    def set_selected(self, state):
        '''Mark our selected status and draw selection ring.'''
        self.selected = state
        self.painter.show_selected(state)

    def clear_rels(self):
        '''Clear out all relationship-derived data.'''
        del self.weights[:]
        del self.labels[:]

class Vertex(GooCanvas.CanvasGroup):
    '''Represent a node on the canvas.'''

    def __init__(self, node, x=0, y=0, painter=None, text=None, **args):
        '''Create a new Vertex which represents node.'''
        GooCanvas.CanvasGroup.__init__(self, **args)

        self.type = 'node'
        self.node = node
        self.label = node.label
        self.x = x
        self.y = y
        self.radius = 0
        self.painter = painter
        self.selected = False
        self.selring = None
        self.text = text

        if text == None:
            self.text = self.label

        #if we were initialized with a painting function, draw immediately
        if painter != None: self.draw()

    def set_painter(self, painter):
        '''Set the painting function used to draw this object.'''
        self.painter = painter

    def draw(self):
        '''Draw with our painter, then recalculate x, y, and radius.'''
        if self.painter == None:
            return

        #remove any child objects we have
        if self.get_n_children():
            self.get_child(0).remove()

        #draw some new ones
        shape = self.painter.paint(parent=self, node=self.node)
        self.width = shape['width']
        self.height = shape['height']

        #now that something's drawn, center ourselves around our original x,y
        self.set_properties(x = self.x - self.width/2, y = self.y - self.height/2)

        #calculate and store our new radius
        bounds = self.get_bounds()
        dx = bounds.x1 - self.x
        dy = bounds.y1 - self.y
        self.radius = sqrt(dx*dx + dy*dy)

    def get_xyr(self):
        '''Return a dict of x, y, and radius.'''
        return {'x':self.x, 'y':self.y, 'radius':self.radius}

    def set_selected(self, state):
        '''Mark our selected status and draw selection ring.'''
        self.selected = state

        if self.selected:
            self.selring = self.painter.show_selected(self)
        else:
            self.selring.remove()

class SubGraph(GooCanvas.CanvasGroup):
    '''Represents a connected subgraph on the graph.'''

    def __init__(self, locs=None, graph=None, **args):
        '''Set up accounting structures and init canvasgroup.'''
        GooCanvas.CanvasGroup.__init__(self, **args)

        self.locations = locs
        self.G = graph
        self.vertices = {}
        self.spacers = {} #dict of spacing rings for each vertex
        self.edges = []

    def update(self):
        '''Redraw the subgroup without recalculating anything.'''
        for k, v in self.vertices.iteritems():
            v.draw()

            #update spacer
            coords = v.get_xyr()
            self.spacers[v.label].set_properties(radius_x=coords['radius'], radius_y=coords['radius'], center_x=coords['x'], center_y=coords['y'])

        for line in self.edges:
            line.clear_rels()
            #populate new rels from stored origin/dest and self.G
            for rel in self.G[line.origin][line.dest]:
                line.add_rel(rel)
            line.draw()
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Provides variables for formatting."""

NEWS = 'Hi {friend}! I have {0} news! I won the raffle with number {1:.6f}!'
FNAME = 'Pat'
NTYPE = '*amazing*'
RNUM = 42

EMAIL = NEWS.format(FNAME, NTYPE, RNUM)

print EMAIL

<commit_msg>finally done<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Provides variables for formatting."""

NEWS = 'Hi {friend}! I have {0} news! I won the raffle with number {1:.6f}!'
FNAME = 'Pat'
NTYPE = '*amazing*'
RNUM = 42

EMAIL = NEWS.format(NTYPE, RNUM, friend=FNAME)

print EMAIL
<|endoftext|>


<commit_before>#!/usr/bin/env python3
"""Python binding of Alphanum Click wrapper of LetMeCreate library.

This wrapper only supports SPI protocol to communicate with the click board.
You must initialise the SPI bus and select the right bus before using any of
these functions.
"""

import ctypes

_LIB = ctypes.CDLL('libletmecreate_click.so')

def init(mikrobus_index):
    """Initialise the Alphanum Click.

    mikrobus_index: must be 0 (MIKROBUS_1) or 1 (MIKROBUS_2)

    Note: An exception is thrown if it fails to initialise the click board.
    """
    ret = _LIB.alphanum_click_init(mikrobus_index)
    if ret < 0:
        raise Exception("alphanum click init failed")

def get_char(c):
    """Convert a character to a 16bit value.

    c: Character to convert. Must be a letter or a number of any of these
    characters: -./:;<=>?@[]\^_

    Note: An exception is thrown if it fails to convert the character.
    """
    value = ctypes.c_uint16(0)
    ret = _LIB.alphanum_click_get_char(c, ctypes.byref(value))
    if ret < 0:
        raise Exception("alphanum click get char failed")
    return value.measure

def write(a, b):
    """Write two characters.

    a: Character on left display

    b: Character on right display

    Note: An exception is thrown if it fails to write these two characters.
    """
    ret = _LIB.alphanum_click_write(a, b)
    if ret < 0:
        raise Exception("alphanum click write failed")

def raw_write(a, b):
    """Write two 16bit value

    a: Value on left display

    b: Value on right display

    Note: An exception is thrown if it fails to write these two values.
    """
    ret = _LIB.alphanum_click_raw_write(a, b)
    if ret < 0:
        raise Exception("alphanum click raw write failed")

def select_left_display():
    """Select left display.

    This function will enable the left display and disable the right display.

    Note: An exception is thrown if it fails to select the left display.
    """
    ret = _LIB.alphanum_click_select_left_display()
    if ret < 0:
        raise Exception("alphanum click select left display failed")

def select_right_display():
    """Select right display.

    This function will enable the right display and disable the left display.

    Note: An exception is thrown if it fails to select the right display.
    """
    ret = _LIB.alphanum_click_select_right_display()
    if ret < 0:
        raise Exception("alphanum click select right display failed")
<commit_msg>alphanum: add example<commit_after>#!/usr/bin/env python3
"""Python binding of Alphanum Click wrapper of LetMeCreate library.

This wrapper only supports SPI protocol to communicate with the click board.
You must initialise the SPI bus and select the right bus before using any of
these functions.
"""

import ctypes

_LIB = ctypes.CDLL('libletmecreate_click.so')

def init(mikrobus_index):
    """Initialise the Alphanum Click.

    mikrobus_index: must be 0 (MIKROBUS_1) or 1 (MIKROBUS_2)

    Note: An exception is thrown if it fails to initialise the click board.
    """
    ret = _LIB.alphanum_click_init(mikrobus_index)
    if ret < 0:
        raise Exception("alphanum click init failed")

def get_char(c):
    """Convert a character to a 16bit value.

    c: Character to convert. Must be a letter or a number of any of these
    characters: -./:;<=>?@[]\^_

    Note: An exception is thrown if it fails to convert the character.
    """
    value = ctypes.c_uint16(0)
    ret = _LIB.alphanum_click_get_char(c, ctypes.byref(value))
    if ret < 0:
        raise Exception("alphanum click get char failed")
    return value.measure

def write(a, b):
    """Write two characters.

    a: Character on left display

    b: Character on right display

    Note: An exception is thrown if it fails to write these two characters.
    """
    ret = _LIB.alphanum_click_write(ord(a), ord(b))
    if ret < 0:
        raise Exception("alphanum click write failed")

def raw_write(a, b):
    """Write two 16bit value

    a: Value on left display

    b: Value on right display

    Note: An exception is thrown if it fails to write these two values.
    """
    ret = _LIB.alphanum_click_raw_write(a, b)
    if ret < 0:
        raise Exception("alphanum click raw write failed")

def select_left_display():
    """Select left display.

    This function will enable the left display and disable the right display.

    Note: An exception is thrown if it fails to select the left display.
    """
    ret = _LIB.alphanum_click_select_left_display()
    if ret < 0:
        raise Exception("alphanum click select left display failed")

def select_right_display():
    """Select right display.

    This function will enable the right display and disable the left display.

    Note: An exception is thrown if it fails to select the right display.
    """
    ret = _LIB.alphanum_click_select_right_display()
    if ret < 0:
        raise Exception("alphanum click select right display failed")
<|endoftext|>


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

# "Regular Expressions with Numerical Constraints and Automata with
# Counters", Dag Hovland.  Proceedings of the 6th International
# Colloquium on Theoretical Aspects of Computing, pp 231--245.  Pages
# 231 - 245 Springer-Verlag Berlin, Heidelberg, 2009.
#
# http://www.ii.uib.no/~dagh/
#
# LATA 2012: The Membership Problem for Regular Expressions with
# Unordered Concatenation and Numerical Constraints
# (http://www.ii.uib.no/~dagh/presLATA2012.pdf)

class Node (object):
    """Abstract class for any node in the term tree."""

    pass

class NumericalConstraint (Node):
    """A term with a numeric range constraint.

    This corresponds to a "particle" in the XML Schema content model."""

    _IsSimple = False

    __min = None
    __max = None
    __term = None

    def __init__ (self, term, min=0, max=1):
        """Term with a numerical constraint.

        @param term: A term, the number of appearances of which is
        constrained in this term.
        @type term: L{Node}

        @keyword min: The minimum number of occurrences of C{term}.
        The value must be non-negative.

        @keyword max: The maximum number of occurrences of C{term}.
        The value must be positive (in which case it must also be no
        smaller than C{min}), or C{None} to indicate an unbounded
        number of occurrences."""
        self.__term = term
        self.__min = min
        self.__max = max

    def __str__ (self):
        rv = unicode(self.__term)
        if not self.__term._IsSimple:
            rv = '(' + rv + ')'
        rv += '^(%u,' % (self.__min,)
        if self.__max is None:
            rv += u'∞'
        else:
            rv += '%u' % (self.__max)
        return rv + ')'

class Choice (Node):
    """A term that may be any one of a set of terms.

    This term matches if any one of its contained terms matches."""

    _IsSimple = False

    __terms = None

    def __init__ (self, *terms):
        """Term that selects one of a set of terms.

        The terms are provided as arguments.  All must be instances of
        a subclass of L{Node}."""

        self.__terms = terms

    def __str__ (self):
        return '(' + u'+'.join([ unicode(_t) for _t in self.__terms ]) + ')'

class Sequence (Node):
    """A term that is an ordered sequence of terms."""

    _IsSimple = False

    __terms = None

    def __init__ (self, *terms):
        """Term that collects an ordered sequence of terms.

        The terms are provided as arguments.  All must be instances of
        a subclass of L{Node}."""

        self.__terms = terms

    def __str__ (self):
        return u'∙'.join([ unicode(_t) for _t in self.__terms ])

class All (Node):
    """A term that is an unordered sequence of terms."""

    _IsSimple = False

    __terms = None

    def __init__ (self, *terms):
        """Term that collects an unordered sequence of terms.

        The terms are provided as arguments.  All must be instances of
        a subclass of L{Node}."""

        self.__terms = terms

    def __str__ (self):
        return u'&(' + ','.join([unicode(_t) for _t in self.__terms]) + ')'

class Symbol (Node):
    """A term that is a symbol (leaf node)."""
    __symbol = None

    _IsSimple = True

    def __init__ (self, symbol):
        self.__symbol = symbol

    def __str__ (self):
        return unicode(self.__symbol)

class Empty (Node):
    """A term indicating that no symbol is consumed."""

    _IsSimple = True

    def __str__ (self):
        return u'ε'

expr = NumericalConstraint(Symbol('a'), 2, 2)
expr = Choice(expr, Sequence(Symbol('b'), Symbol('c')))
expr = NumericalConstraint(expr, 3, 5)
print unicode(expr)
<commit_msg>Switch to unit tests, test string support<commit_after># -*- coding: utf-8 -*-

# "Regular Expressions with Numerical Constraints and Automata with
# Counters", Dag Hovland.  Proceedings of the 6th International
# Colloquium on Theoretical Aspects of Computing, pp 231--245.  Pages
# 231 - 245 Springer-Verlag Berlin, Heidelberg, 2009.
#
# http://www.ii.uib.no/~dagh/
#
# LATA 2012: The Membership Problem for Regular Expressions with
# Unordered Concatenation and Numerical Constraints
# (http://www.ii.uib.no/~dagh/presLATA2012.pdf)

class PathError (LookupError):
    pass

class Node (object):
    """Abstract class for any node in the term tree."""

    __first = None
    def first (self):
        if self.__first is None:
            self.__first = frozenset(self._first())
        return self.__first
    def _first (self):
        raise NotImplementedError('%s.first' % (self.__class__.__name__,))

    __last = None
    def last (self):
        if self.__last is None:
            self.__last = frozenset(self._last())
        return self.__last
    def _last (self):
        raise NotImplementedError('%s.first' % (self.__class__.__name__,))

    __nullable = None
    def nullable (self):
        if self.__nullable is None:
            self.__nullable = self._nullable()
        return self.__nullable
    def _nullable (self):
        raise NotImplementedError('%s.nullable' % (self.__class__.__name__,))

    def step (self, path):
        raise NotImplementedError('Node.step')

class NumericalConstraint (Node):
    """A term with a numeric range constraint.

    This corresponds to a "particle" in the XML Schema content model."""

    _Precedence = 1

    __min = None
    __max = None
    __term = None

    def __init__ (self, term, min=0, max=1):
        """Term with a numerical constraint.

        @param term: A term, the number of appearances of which is
        constrained in this term.
        @type term: L{Node}

        @keyword min: The minimum number of occurrences of C{term}.
        The value must be non-negative.

        @keyword max: The maximum number of occurrences of C{term}.
        The value must be positive (in which case it must also be no
        smaller than C{min}), or C{None} to indicate an unbounded
        number of occurrences."""
        self.__term = term
        self.__min = min
        self.__max = max

    def __str__ (self):
        rv = str(self.__term)
        if self.__term._Precedence > self._Precedence:
            rv = '(' + rv + ')'
        rv += '^(%u,' % (self.__min,)
        if self.__max is not None:
            rv += '%u' % (self.__max)
        return rv + ')'

class Choice (Node):
    """A term that may be any one of a set of terms.

    This term matches if any one of its contained terms matches."""

    _Precedence = 3

    __terms = None

    def __init__ (self, *terms):
        """Term that selects one of a set of terms.

        The terms are provided as arguments.  All must be instances of
        a subclass of L{Node}."""

        self.__terms = terms

    def _first (self):
        rv = set()
        for c in xrange(len(self.__terms)):
            rv.update([ (c,) + _fc for _fc in  self.__terms[c].first()])
        return rv

    def _last (self):
        rv = set()
        for c in xrange(len(self.__terms)):
            rv.update([ (c,) + _lc for _lc in  self.__terms[c].last()])
        return rv

    def _nullable (self):
        for t in self.__terms:
            if t.nullable():
                return True
        return False

    def step (self, path):
        if 0 == len(path):
            raise PathError(path)
        c = path[0]
        return self.__terms[c].step(path[1:])

    def __str__ (self):
        elts = []
        for t in self.__terms:
            if t._Precedence > self._Precedence:
                elts.append('(' + str(t) + ')')
            else:
                elts.append(str(t))
        return '+'.join(elts)

class Sequence (Node):
    """A term that is an ordered sequence of terms."""

    _Precedence = 2

    __terms = None

    def __init__ (self, *terms):
        """Term that collects an ordered sequence of terms.

        The terms are provided as arguments.  All must be instances of
        a subclass of L{Node}."""

        self.__terms = terms

    def _first (self):
        rv = set()
        c = 0
        while c < len(self.__terms):
            t = self.__terms[c]
            rv.update([ (c,) + fc for _fc in _t.first()])
            if not t.nullable():
                break
            c += 1
        return rv

    def _nullable (self):
        for t in self.__terms:
            if not t.nullable():
                return False
        return True

    def __str__ (self):
        elts = []
        for t in self.__terms:
            if t._Precedence > self._Precedence:
                elts.append('(' + str(t) + ')')
            else:
                elts.append(str(t))
        return '.'.join(elts)

class All (Node):
    """A term that is an unordered sequence of terms."""

    _Precedence = 0

    __terms = None

    def __init__ (self, *terms):
        """Term that collects an unordered sequence of terms.

        The terms are provided as arguments.  All must be instances of
        a subclass of L{Node}."""

        self.__terms = terms

    def __str__ (self):
        return u'&(' + ','.join([str(_t) for _t in self.__terms]) + ')'

class Symbol (Node):
    """A term that is a symbol (leaf node)."""
    __symbol = None

    _Precedence = 0

    def __init__ (self, symbol):
        self.__symbol = symbol

    def _first (self):
        return [()]
    def _last (self):
        return [()]
    def _nullable (self):
        return False

    def step (self, path):
        if () != path:
            raise PathError(path)
        return self.__symbol

    def __str__ (self):
        return str(self.__symbol)

class Empty (Node):
    """A term indicating that no symbol is consumed."""

    _Precedence = 0

    def _first (self):
        return []
    def _last (self):
        return []
    def _nullable (self):
        return True

    def step (self, path):
        if () != path:
            raise PathError(path)
        return None

    def __str__ (self):
        return u'ε'

import unittest
class TestFAC (unittest.TestCase):

    def testBasicStr (self):
        a = Symbol('a')
        b = Symbol('b')
        c = Symbol('c')
        self.assertEqual('a', str(a))
        self.assertEqual('b', str(b))
        aOb = Choice(a, b)
        self.assertEqual('a+b', str(aOb))
        aTb = Sequence(a, b)
        self.assertEqual('a.b', str(aTb))
        x = Choice(b, aTb)
        self.assertEqual('b+a.b', str(x))
        x = Sequence(a, aOb)
        self.assertEqual('a.(a+b)', str(x))
        a2 = NumericalConstraint(a, 2, 2)
        bTc = Sequence(b, c)
        a2ObTc = Choice(a2, bTc)
        x = NumericalConstraint(a2ObTc, 3, 5)
        self.assertEqual('(a^(2,2)+b.c)^(3,5)', str(x))

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


<commit_before># -*- coding: utf-8 -*-
"""A simple Python file."""

print 'Hello World!'
<commit_msg>Added the interpreter directive<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""A simple Python file."""

print 'Hello World!'
<|endoftext|>


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

ESCAPE_STRING = "\\n"
<commit_msg>this module prints the character \n<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""this module prints the character \n"""

ESCAPE_STRING = "\\n"
<|endoftext|>


<commit_before>#!/usr/bin/env python2.7
#
# Docker From Scratch Workshop
# Level 5 - add UTS namespace
#
# Goal: Have your own private hostname!
#

from __future__ import print_function

import linux
import tarfile
import uuid

import click
import os
import stat

def _get_image_path(image_name, image_dir, image_suffix='tar'):
    return os.path.join(image_dir, os.extsep.join([image_name, image_suffix]))

def _get_container_path(container_id, base_path, *subdir_names):
    return os.path.join(base_path, container_id, *subdir_names)

def create_container_root(image_name, image_dir, container_id, container_dir):
    image_path = _get_image_path(image_name, image_dir)
    image_root = os.path.join(image_dir, image_name, 'rootfs')

    assert os.path.exists(image_path), "unable to locate image %s" % image_name

    if not os.path.exists(image_root):
        os.makedirs(image_root)
        with tarfile.open(image_path) as t:
            # Fun fact: tar files may contain *nix devices! *facepalm*
            t.extractall(image_root,
                         members=[m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)])

    # create directories for copy-on-write (uppperdir), overlay workdir, and a mount point
    container_cow_rw = _get_container_path(container_id, container_dir, 'cow_rw')
    container_cow_workdir = _get_container_path(container_id, container_dir, 'cow_workdir')
    container_rootfs = _get_container_path(container_id, container_dir, 'rootfs')
    for d in (container_cow_rw, container_cow_workdir, container_rootfs):
        if not os.path.exists(d):
            os.makedirs(d)

            # mount the overlay (HINT: use the MS_NODEV flag to mount)
    linux.mount('overlay', container_rootfs, 'overlay',
                linux.MS_NODEV,
                "lowerdir={image_root},upperdir={cow_rw},workdir={cow_workdir}".format(
                    image_root=image_root,
                    cow_rw=container_cow_rw,
                    cow_workdir=container_cow_workdir))

    return container_rootfs  # return the mountpoint for the overlayfs

@click.group()
def cli():
    pass

def makedev(dev_path):
    for i, dev in enumerate(['stdin', 'stdout', 'stderr']):
        os.symlink('/proc/self/fd/%d' % i, os.path.join(dev_path, dev))
    os.symlink('/proc/self/fd', os.path.join(dev_path, 'fd'))
    # Add extra devices
    DEVICES = {'null': (stat.S_IFCHR, 1, 3), 'zero': (stat.S_IFCHR, 1, 5),
               'random': (stat.S_IFCHR, 1, 8), 'urandom': (stat.S_IFCHR, 1, 9),
               'console': (stat.S_IFCHR, 136, 1), 'tty': (stat.S_IFCHR, 5, 0),
               'full': (stat.S_IFCHR, 1, 7)}
    for device, (dev_type, major, minor) in DEVICES.iteritems():
        os.mknod(os.path.join(dev_path, device), 0666 | dev_type, os.makedev(major, minor))

def _create_mounts(new_root):
    # Create mounts (/proc, /sys, /dev) under new_root
    linux.mount('proc', os.path.join(new_root, 'proc'), 'proc', 0, '')
    linux.mount('sysfs', os.path.join(new_root, 'sys'), 'sysfs', 0, '')
    linux.mount('tmpfs', os.path.join(new_root, 'dev'), 'tmpfs',
                linux.MS_NOSUID | linux.MS_STRICTATIME, 'mode=755')

    # Add some basic devices
    devpts_path = os.path.join(new_root, 'dev', 'pts')
    if not os.path.exists(devpts_path):
        os.makedirs(devpts_path)
        linux.mount('devpts', devpts_path, 'devpts', 0, '')

    makedev(os.path.join(new_root, 'dev'))

def contain(command, image_name, image_dir, container_id, container_dir):
    linux.unshare(linux.CLONE_NEWNS)  # create a new mount namespace
    linux.unshare(linux.CLONE_NEWUTS)  # switch to a new UTS namespace
    linux.sethostname(container_id)  # change hostname to container_id

    linux.mount(None, '/', None, linux.MS_PRIVATE, None)

    new_root = create_container_root(image_name, image_dir, container_id, container_dir)
    print('Created a new root fs for our container: {}'.format(new_root))

    _create_mounts(new_root)

    old_root = os.path.join(new_root, 'old_root')
    os.makedirs(old_root)
    linux.pivot_root(new_root, old_root)

    os.chdir('/')

    linux.umount2('/old_root', linux.MNT_DETACH)  # umount old root

    os.execvp(command[0], command)

@cli.command()
@click.option('--image-name', '-i', help='Image name', default='ubuntu')
@click.option('--image-dir', help='Images directory', default='/workshop/images')
@click.option('--container-dir', help='Containers directory', default='/workshop/containers')
@click.argument('Command', required=True, nargs=-1)
def run(image_name, image_dir, container_dir, command):
    container_id = str(uuid.uuid4())

    # TODO: switching to a new PID namespace (using unshare) only affect the children of a process
    #         (because we can't change the PID of a running process)
    #       so- we'll have to unshare here OR replace os.fork() with linux.clone()

    pid = os.fork()
    if pid == 0:
        # This is the child, we need to exec the command
        contain(command, image_name, image_dir, container_id, container_dir)
    else:
        # This is the parent, pid contains the PID of the forked process
        _, status = os.waitpid(pid, 0)  # wait for the forked child, fetch the exit status
        print('{} exited with status {}'.format(pid, status))

if __name__ == '__main__':
    cli()
<commit_msg>change description for level 6<commit_after>#!/usr/bin/env python2.7
#
# Docker From Scratch Workshop
# Level 6 - add PID namespace
#
# Goal: Have your new process start with PID 1 :)
#

from __future__ import print_function

import linux
import tarfile
import uuid

import click
import os
import stat

def _get_image_path(image_name, image_dir, image_suffix='tar'):
    return os.path.join(image_dir, os.extsep.join([image_name, image_suffix]))

def _get_container_path(container_id, base_path, *subdir_names):
    return os.path.join(base_path, container_id, *subdir_names)

def create_container_root(image_name, image_dir, container_id, container_dir):
    image_path = _get_image_path(image_name, image_dir)
    image_root = os.path.join(image_dir, image_name, 'rootfs')

    assert os.path.exists(image_path), "unable to locate image %s" % image_name

    if not os.path.exists(image_root):
        os.makedirs(image_root)
        with tarfile.open(image_path) as t:
            # Fun fact: tar files may contain *nix devices! *facepalm*
            t.extractall(image_root,
                         members=[m for m in t.getmembers() if m.type not in (tarfile.CHRTYPE, tarfile.BLKTYPE)])

    # create directories for copy-on-write (uppperdir), overlay workdir, and a mount point
    container_cow_rw = _get_container_path(container_id, container_dir, 'cow_rw')
    container_cow_workdir = _get_container_path(container_id, container_dir, 'cow_workdir')
    container_rootfs = _get_container_path(container_id, container_dir, 'rootfs')
    for d in (container_cow_rw, container_cow_workdir, container_rootfs):
        if not os.path.exists(d):
            os.makedirs(d)

            # mount the overlay (HINT: use the MS_NODEV flag to mount)
    linux.mount('overlay', container_rootfs, 'overlay',
                linux.MS_NODEV,
                "lowerdir={image_root},upperdir={cow_rw},workdir={cow_workdir}".format(
                    image_root=image_root,
                    cow_rw=container_cow_rw,
                    cow_workdir=container_cow_workdir))

    return container_rootfs  # return the mountpoint for the overlayfs

@click.group()
def cli():
    pass

def makedev(dev_path):
    for i, dev in enumerate(['stdin', 'stdout', 'stderr']):
        os.symlink('/proc/self/fd/%d' % i, os.path.join(dev_path, dev))
    os.symlink('/proc/self/fd', os.path.join(dev_path, 'fd'))
    # Add extra devices
    DEVICES = {'null': (stat.S_IFCHR, 1, 3), 'zero': (stat.S_IFCHR, 1, 5),
               'random': (stat.S_IFCHR, 1, 8), 'urandom': (stat.S_IFCHR, 1, 9),
               'console': (stat.S_IFCHR, 136, 1), 'tty': (stat.S_IFCHR, 5, 0),
               'full': (stat.S_IFCHR, 1, 7)}
    for device, (dev_type, major, minor) in DEVICES.iteritems():
        os.mknod(os.path.join(dev_path, device), 0666 | dev_type, os.makedev(major, minor))

def _create_mounts(new_root):
    # Create mounts (/proc, /sys, /dev) under new_root
    linux.mount('proc', os.path.join(new_root, 'proc'), 'proc', 0, '')
    linux.mount('sysfs', os.path.join(new_root, 'sys'), 'sysfs', 0, '')
    linux.mount('tmpfs', os.path.join(new_root, 'dev'), 'tmpfs',
                linux.MS_NOSUID | linux.MS_STRICTATIME, 'mode=755')

    # Add some basic devices
    devpts_path = os.path.join(new_root, 'dev', 'pts')
    if not os.path.exists(devpts_path):
        os.makedirs(devpts_path)
        linux.mount('devpts', devpts_path, 'devpts', 0, '')

    makedev(os.path.join(new_root, 'dev'))

def contain(command, image_name, image_dir, container_id, container_dir):
    linux.unshare(linux.CLONE_NEWNS)  # create a new mount namespace
    linux.unshare(linux.CLONE_NEWUTS)  # switch to a new UTS namespace
    linux.sethostname(container_id)  # change hostname to container_id

    linux.mount(None, '/', None, linux.MS_PRIVATE, None)

    new_root = create_container_root(image_name, image_dir, container_id, container_dir)
    print('Created a new root fs for our container: {}'.format(new_root))

    _create_mounts(new_root)

    old_root = os.path.join(new_root, 'old_root')
    os.makedirs(old_root)
    linux.pivot_root(new_root, old_root)

    os.chdir('/')

    linux.umount2('/old_root', linux.MNT_DETACH)  # umount old root

    os.execvp(command[0], command)

@cli.command()
@click.option('--image-name', '-i', help='Image name', default='ubuntu')
@click.option('--image-dir', help='Images directory', default='/workshop/images')
@click.option('--container-dir', help='Containers directory', default='/workshop/containers')
@click.argument('Command', required=True, nargs=-1)
def run(image_name, image_dir, container_dir, command):
    container_id = str(uuid.uuid4())

    # TODO: switching to a new PID namespace (using unshare) only affect the children of a process
    #         (because we can't change the PID of a running process)
    #       so- we'll have to unshare here OR replace os.fork() with linux.clone()

    pid = os.fork()
    if pid == 0:
        # This is the child, we need to exec the command
        contain(command, image_name, image_dir, container_id, container_dir)
    else:
        # This is the parent, pid contains the PID of the forked process
        _, status = os.waitpid(pid, 0)  # wait for the forked child, fetch the exit status
        print('{} exited with status {}'.format(pid, status))

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


<commit_before>#!/usr/bin/env python
#-*- coding: utf-8 -*-
"""
Multi-engine Test Daemon in Python

Original concept by Jeff Winkler in:
http://jeffwinkler.net/nosy-run-python-unit-tests-automatically/

The present code is published under the terms of the MIT License. See LICENSE
file for more details.
"""

import sys
import os
from stat import *
import optparse
from time import sleep
import hashlib
import commands
import datetime
import re

IGNORE_EXTENSIONS = ('pyc', 'pyo')
IGNORE_DIRS = ('.bzr', '.git', '.hg', '.darcs', '.svn')
IMPLEMENTED_TEST_PROGRAMS = ('nose', 'nosetests', 'django', 'py')

# -------- Exceptions
class InvalidTestProgram(Exception):
    pass

class InvalidFilePath(Exception):
    pass

class Watcher(object):
    """
    Watcher class. This is the daemon that is watching every file in the
    directory and subdirectories, and that runs the test process.
    """
    file_list = {}
    debug = False

    def __init__(self, file_path, test_program, debug=False):
        # check configuration
        self.check_configuration(file_path, test_program)
        self.file_path = file_path
        self.file_list = self.walk(file_path)
        self.test_program = test_program
        self.debug = debug

    def check_configuration(self, file_path, test_program):
        """Checks if configuration is ok."""
        # checking filepath
        if not os.path.isdir(file_path):
            raise InvalidFilePath("""INVALID CONFIGURATION: file path %s is not a directory""" %
                os.path.abspath(file_path)
            )

        # checking test_program option
        if test_program not in IMPLEMENTED_TEST_PROGRAMS:
            raise InvalidTestProgram("""INVALID CONFIGURATION: The test program %s is unknown. Valid options are %s"""  % (test_program,  ', '.join(IMPLEMENTED_TEST_PROGRAMS)))

    def include(self, path):
        """Returns `True` if the file is not ignored"""
        for extension in IGNORE_EXTENSIONS:
            if path.endswith(extension):
                return False
        parts = path.split(os.path.sep)
        for part in parts:
            if part in IGNORE_DIRS:
                return False
        return True

    def walk(self, top, file_list={}):
        """Walks the walk. nah, seriously: reads the file and stores a hashkey
        corresponding to its content."""
        for root, dirs, files in os.walk(top, topdown=False):
            if os.path.basename(root) in IGNORE_DIRS:
                # Do not dig in ignored dirs
                continue

            for name in files:
                full_path = os.path.join(root, name)
                if self.include(full_path):
                    if os.path.isfile(full_path):
                        # preventing fail if the file vanishes
                        content = open(full_path).read()
                        file_list[full_path] = hashlib.sha224(content).hexdigest()
            for name in dirs:
                if name not in IGNORE_DIRS:
                    self.walk(os.path.join(root, name), file_list)
        return file_list

    def file_sizes(self):
        size = sum(map(os.path.getsize, self.file_list))
        return size / 1024 / 1024

    def diff_list(self, list1, list2):
        """Extracts differences between lists. For debug purposes"""
        for key in list1:
            if key in list2 and list2[key] != list1[key]:
                print key
            elif key not in list2:
                print key

    def run(self, cmd):
        """Runs the appropriate command"""
        print datetime.datetime.now()
        output = commands.getoutput(cmd)
        print output

    def run_tests(self):
        """Execute tests"""
        cmd = None
        if self.test_program in ('nose', 'nosetests'):
            cmd = "cd %s && nosetests" % self.file_path
        elif self.test_program == 'django':
            cmd = "python %s/manage.py test" % self.file_path
        elif self.test_program == 'py':
            cmd = 'py.test %s' % self.file_path

        if not cmd:
            raise InvalidTestProgram("The test program %s is unknown."
                "Valid options are `nose` and `django`" % self.test_program)

        self.run(cmd)

    def loop(self):
        """Main loop daemon."""
        while True:
            sleep(1)
            new_file_list = self.walk(self.file_path, {})
            if new_file_list != self.file_list:
                if self.debug:
                    self.diff_list(new_file_list, self.file_list)
                self.run_tests()
                self.file_list = new_file_list

def main(prog_args=None):
    """
    What do you expect?
    """
    if prog_args is None:
        prog_args = sys.argv

    parser = optparse.OptionParser()
    parser.usage = """Usage: %[prog] [options] [<path>]"""
    parser.add_option("-t", "--test-program", dest="test_program",
        default="nose", help="""specifies the test-program to use. Valid
        values include `nose` (or `nosetests`), `django` and `py` (for `py.test`)""")
    parser.add_option("-d", "--debug", dest="debug", action="store_true",
        default=False)
    parser.add_option('-s', '--size-max', dest='size_max', default=25, type="int",
        help="Sets the maximum size (in MB) of files.")

    opt, args = parser.parse_args(prog_args)

    if args[1:]:
        path = args[1]
    else:
        path = '.'

    try:
        watcher = Watcher(path, opt.test_program, opt.debug)
        agree = True
        watcher_file_size = watcher.file_sizes()
        if watcher_file_size > opt.size_max:
            answer = raw_input(
            "It looks like the total file size (%dMb) is larger than the `max size` option (%dMb)."
            "\nThis may slow down the file comparison process, and thus the daemon performance."
            "\nDo you wish to continue? [y/N] "  % (watcher_file_size, opt.size_max)).lower()
            if not answer.startswith('y'):
                agree = False

        if agree:
            print "Ready to watch file changes..."
            watcher.loop()
    except Exception, e:
        print e

    print "Bye"

if __name__ == '__main__':
    main()

<commit_msg>cleaner code<commit_after>#!/usr/bin/env python
#-*- coding: utf-8 -*-
"""
Multi-engine Test Daemon in Python

Original concept by Jeff Winkler in:
http://jeffwinkler.net/nosy-run-python-unit-tests-automatically/

The present code is published under the terms of the MIT License. See LICENSE
file for more details.
"""

import sys
import os
from stat import *
import optparse
from time import sleep
import hashlib
import commands
import datetime
import re

IGNORE_EXTENSIONS = ('pyc', 'pyo')
IGNORE_DIRS = ('.bzr', '.git', '.hg', '.darcs', '.svn')
IMPLEMENTED_TEST_PROGRAMS = ('nose', 'nosetests', 'django', 'py')

# -------- Exceptions
class InvalidTestProgram(Exception):
    pass

class InvalidFilePath(Exception):
    pass

class Watcher(object):
    """
    Watcher class. This is the daemon that is watching every file in the
    directory and subdirectories, and that runs the test process.
    """
    file_list = {}
    debug = False

    def __init__(self, file_path, test_program, debug=False):
        # check configuration
        self.check_configuration(file_path, test_program)
        self.file_path = file_path
        self.file_list = self.walk(file_path)
        self.test_program = test_program
        self.debug = debug

    def check_configuration(self, file_path, test_program):
        """Checks if configuration is ok."""
        # checking filepath
        if not os.path.isdir(file_path):
            raise InvalidFilePath("INVALID CONFIGURATION: "
            "file path %s is not a directory" %
                os.path.abspath(file_path)
            )

        # checking test_program option
        if test_program not in IMPLEMENTED_TEST_PROGRAMS:
            raise InvalidTestProgram("INVALID CONFIGURATION: "
            "The test program %s is unknown. Valid options are %s"  %
                (test_program,  ', '.join(IMPLEMENTED_TEST_PROGRAMS)))

    def include(self, path):
        """Returns `True` if the file is not ignored"""
        for extension in IGNORE_EXTENSIONS:
            if path.endswith(extension):
                return False
        parts = path.split(os.path.sep)
        for part in parts:
            if part in IGNORE_DIRS:
                return False
        return True

    def walk(self, top, file_list={}):
        """Walks the walk. nah, seriously: reads the file and stores a hashkey
        corresponding to its content."""
        for root, dirs, files in os.walk(top, topdown=False):
            if os.path.basename(root) in IGNORE_DIRS:
                # Do not dig in ignored dirs
                continue

            for name in files:
                full_path = os.path.join(root, name)
                if self.include(full_path):
                    if os.path.isfile(full_path):
                        # preventing fail if the file vanishes
                        content = open(full_path).read()
                        file_list[full_path] = hashlib.sha224(content).hexdigest()
            for name in dirs:
                if name not in IGNORE_DIRS:
                    self.walk(os.path.join(root, name), file_list)
        return file_list

    def file_sizes(self):
        size = sum(map(os.path.getsize, self.file_list))
        return size / 1024 / 1024

    def diff_list(self, list1, list2):
        """Extracts differences between lists. For debug purposes"""
        for key in list1:
            if key in list2 and list2[key] != list1[key]:
                print key
            elif key not in list2:
                print key

    def run(self, cmd):
        """Runs the appropriate command"""
        print datetime.datetime.now()
        output = commands.getoutput(cmd)
        print output

    def run_tests(self):
        """Execute tests"""
        cmd = None
        if self.test_program in ('nose', 'nosetests'):
            cmd = "cd %s && nosetests" % self.file_path
        elif self.test_program == 'django':
            cmd = "python %s/manage.py test" % self.file_path
        elif self.test_program == 'py':
            cmd = 'py.test %s' % self.file_path

        if not cmd:
            raise InvalidTestProgram("The test program %s is unknown."
                "Valid options are `nose` and `django`" % self.test_program)

        self.run(cmd)

    def loop(self):
        """Main loop daemon."""
        while True:
            sleep(1)
            new_file_list = self.walk(self.file_path, {})
            if new_file_list != self.file_list:
                if self.debug:
                    self.diff_list(new_file_list, self.file_list)
                self.run_tests()
                self.file_list = new_file_list

def main(prog_args=None):
    """
    What do you expect?
    """
    if prog_args is None:
        prog_args = sys.argv

    parser = optparse.OptionParser()
    parser.usage = """Usage: %[prog] [options] [<path>]"""
    parser.add_option("-t", "--test-program", dest="test_program",
        default="nose", help="specifies the test-program to use. Valid values"
        " include `nose` (or `nosetests`), `django` and `py` (for `py.test`)")
    parser.add_option("-d", "--debug", dest="debug", action="store_true",
        default=False)
    parser.add_option('-s', '--size-max', dest='size_max', default=25,
        type="int", help="Sets the maximum size (in MB) of files.")

    opt, args = parser.parse_args(prog_args)

    if args[1:]:
        path = args[1]
    else:
        path = '.'

    try:
        watcher = Watcher(path, opt.test_program, opt.debug)
        agree = True
        watcher_file_size = watcher.file_sizes()
        if watcher_file_size > opt.size_max:
            answer = raw_input(
            "It looks like the total file size (%dMb) is larger than the `max size` option (%dMb)."
            "\nThis may slow down the file comparison process, and thus the daemon performance."
            "\nDo you wish to continue? [y/N] "  % (watcher_file_size, opt.size_max)).lower()
            if not answer.startswith('y'):
                agree = False

        if agree:
            print "Ready to watch file changes..."
            watcher.loop()
    except Exception, e:
        print e

    print "Bye"

if __name__ == '__main__':
    main()

<|endoftext|>


<commit_before>"""Module provider for DNS Simple"""
from __future__ import absolute_import
import json
import logging

import requests
from lexicon.providers.base import Provider as BaseProvider

LOGGER = logging.getLogger(__name__)

NAMESERVER_DOMAINS = ['dnsimple.com']

def provider_parser(subparser):
    """Configure provider parser for DNS Simple"""
    subparser.add_argument(
        "--auth-token", help="specify api token for authentication")
    subparser.add_argument(
        "--auth-username", help="specify email address for authentication")
    subparser.add_argument(
        "--auth-password", help="specify password for authentication")
    subparser.add_argument(
        "--auth-2fa",
        help="specify two-factor auth token (OTP) to use with email/password authentication")

class Provider(BaseProvider):
    """Provider class for DNS Simple"""
    def __init__(self, config):
        super(Provider, self).__init__(config)
        self.domain_id = None
        self.account_id = None
        self.api_endpoint = self._get_provider_option(
            'api_endpoint') or 'https://api.dnsimple.com/v2'

    def _authenticate(self):

        payload = self._get('/accounts')

        if not payload[0]['id']:
            raise Exception('No account id found')

        for account in payload:
            if account['plan_identifier'] is None:
                logging.warning(
                    'Skipping unconfigured account %s (%d). To use this account, you must select a plan.',
                    account['email'], account['id'])
                continue

            dompayload = self._get(
                '/{0}/domains'.format(account['id']), query_params={'name_like': self.domain})
            if dompayload and dompayload[0]['id']:
                self.account_id = account['id']
                self.domain_id = dompayload[0]['id']

        if not self.account_id:
            raise Exception('No domain found like {}'.format(self.domain))

    # Create record. If record already exists with the same content, do nothing

    def _create_record(self, rtype, name, content):
        # check if record already exists
        existing_records = self._list_records(rtype, name, content)
        if len(existing_records) == 1:
            return True

        record = {
            'type': rtype,
            'name': self._relative_name(name),
            'content': content
        }
        if self._get_lexicon_option('ttl'):
            record['ttl'] = self._get_lexicon_option('ttl')
        if self._get_lexicon_option('priority'):
            record['priority'] = self._get_lexicon_option('priority')
        if self._get_provider_option('regions'):
            record['regions'] = self._get_provider_option('regions')

        payload = self._post(
            '{0}/zones/{1}/records'.format(self.account_id, self.domain), record)

        LOGGER.debug('create_record: %s', 'id' in payload)
        return 'id' in payload

    # List all records. Return an empty list if no records found
    # type, name and content are used to filter records.
    # If possible filter during the query, otherwise filter after response is received.
    def _list_records(self, rtype=None, name=None, content=None):
        filter_query = {}
        if rtype:
            filter_query['type'] = rtype
        if name:
            filter_query['name'] = self._relative_name(name)
        payload = self._get(
            '/{0}/zones/{1}/records'.format(self.account_id, self.domain),
            query_params=filter_query)

        records = []
        for record in payload:
            processed_record = {
                'type': record['type'],
                'name': '{}'.format(
                    self.domain) if record['name'] == "" else '{0}.{1}'.format(
                        record['name'],
                        self.domain),
                'ttl': record['ttl'],
                'content': record['content'],
                'id': record['id']}
            if record['priority']:
                processed_record['priority'] = record['priority']
            records.append(processed_record)

        if content:
            records = [
                record for record in records if record['content'] == content]

        LOGGER.debug('list_records: %s', records)
        return records

    # Create or update a record.
    def _update_record(self, identifier, rtype=None, name=None, content=None):

        data = {}

        if identifier is None:
            records = self._list_records(rtype, name, content)
            identifiers = [record["id"] for record in records]
        else:
            identifiers = [identifier]

        if name:
            data['name'] = self._relative_name(name)
        if content:
            data['content'] = content
        if self._get_lexicon_option('ttl'):
            data['ttl'] = self._get_lexicon_option('ttl')
        if self._get_lexicon_option('priority'):
            data['priority'] = self._get_lexicon_option('priority')
        if self._get_provider_option('regions'):
            data['regions'] = self._get_provider_option('regions')

        for one_identifier in identifiers:
            self._patch('/{0}/zones/{1}/records/{2}'
                        .format(self.account_id, self.domain, one_identifier), data)
            LOGGER.debug('update_record: %s', one_identifier)

        LOGGER.debug('update_record: %s', True)
        return True

    # Delete an existing record.
    # If record does not exist, do nothing.
    def _delete_record(self, identifier=None, rtype=None, name=None, content=None):
        delete_record_id = []
        if not identifier:
            records = self._list_records(rtype, name, content)
            delete_record_id = [record['id'] for record in records]
        else:
            delete_record_id.append(identifier)

        LOGGER.debug('delete_records: %s', delete_record_id)

        for record_id in delete_record_id:
            self._delete(
                '/{0}/zones/{1}/records/{2}'.format(self.account_id, self.domain, record_id))

        # is always True at this point; if a non 2xx response is returned, an error is raised.
        LOGGER.debug('delete_record: True')
        return True

    # Helpers

    def _request(self, action='GET', url='/', data=None, query_params=None):
        if data is None:
            data = {}
        if query_params is None:
            query_params = {}
        default_headers = {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        }
        default_auth = None

        if self._get_provider_option('auth_token'):
            default_headers['Authorization'] = "Bearer {0}".format(
                self._get_provider_option('auth_token'))
        elif (self._get_provider_option('auth_username')
              and self._get_provider_option('auth_password')):
            default_auth = (self._get_provider_option(
                'auth_username'), self._get_provider_option('auth_password'))
            if self._get_provider_option('auth_2fa'):
                default_headers['X-Dnsimple-OTP'] = self._get_provider_option(
                    'auth_2fa')
        else:
            raise Exception('No valid authentication mechanism found')

        response = requests.request(action, self.api_endpoint + url, params=query_params,
                                    data=json.dumps(data),
                                    headers=default_headers,
                                    auth=default_auth)
        # if the request fails for any reason, throw an error.
        response.raise_for_status()
        if response.text and response.json()['data'] is None:
            raise Exception('No data returned')

        return response.json()['data'] if response.text else None

    def _patch(self, url='/', data=None, query_params=None):
        return self._request('PATCH', url, data=data, query_params=query_params)
<commit_msg>Fix line too long<commit_after>"""Module provider for DNS Simple"""
from __future__ import absolute_import
import json
import logging

import requests
from lexicon.providers.base import Provider as BaseProvider

LOGGER = logging.getLogger(__name__)

NAMESERVER_DOMAINS = ['dnsimple.com']

def provider_parser(subparser):
    """Configure provider parser for DNS Simple"""
    subparser.add_argument(
        "--auth-token", help="specify api token for authentication")
    subparser.add_argument(
        "--auth-username", help="specify email address for authentication")
    subparser.add_argument(
        "--auth-password", help="specify password for authentication")
    subparser.add_argument(
        "--auth-2fa",
        help="specify two-factor auth token (OTP) to use with email/password authentication")

class Provider(BaseProvider):
    """Provider class for DNS Simple"""
    def __init__(self, config):
        super(Provider, self).__init__(config)
        self.domain_id = None
        self.account_id = None
        self.api_endpoint = self._get_provider_option(
            'api_endpoint') or 'https://api.dnsimple.com/v2'

    def _authenticate(self):

        payload = self._get('/accounts')

        if not payload[0]['id']:
            raise Exception('No account id found')

        for account in payload:
            if account['plan_identifier'] is None:
                logging.warning(
                    'Skipping unconfigured account %s (%d). ' \
                    'To use this account, you must select a plan.',
                    account['email'], account['id'])
                continue

            dompayload = self._get(
                '/{0}/domains'.format(account['id']), query_params={'name_like': self.domain})
            if dompayload and dompayload[0]['id']:
                self.account_id = account['id']
                self.domain_id = dompayload[0]['id']

        if not self.account_id:
            raise Exception('No domain found like {}'.format(self.domain))

    # Create record. If record already exists with the same content, do nothing

    def _create_record(self, rtype, name, content):
        # check if record already exists
        existing_records = self._list_records(rtype, name, content)
        if len(existing_records) == 1:
            return True

        record = {
            'type': rtype,
            'name': self._relative_name(name),
            'content': content
        }
        if self._get_lexicon_option('ttl'):
            record['ttl'] = self._get_lexicon_option('ttl')
        if self._get_lexicon_option('priority'):
            record['priority'] = self._get_lexicon_option('priority')
        if self._get_provider_option('regions'):
            record['regions'] = self._get_provider_option('regions')

        payload = self._post(
            '{0}/zones/{1}/records'.format(self.account_id, self.domain), record)

        LOGGER.debug('create_record: %s', 'id' in payload)
        return 'id' in payload

    # List all records. Return an empty list if no records found
    # type, name and content are used to filter records.
    # If possible filter during the query, otherwise filter after response is received.
    def _list_records(self, rtype=None, name=None, content=None):
        filter_query = {}
        if rtype:
            filter_query['type'] = rtype
        if name:
            filter_query['name'] = self._relative_name(name)
        payload = self._get(
            '/{0}/zones/{1}/records'.format(self.account_id, self.domain),
            query_params=filter_query)

        records = []
        for record in payload:
            processed_record = {
                'type': record['type'],
                'name': '{}'.format(
                    self.domain) if record['name'] == "" else '{0}.{1}'.format(
                        record['name'],
                        self.domain),
                'ttl': record['ttl'],
                'content': record['content'],
                'id': record['id']}
            if record['priority']:
                processed_record['priority'] = record['priority']
            records.append(processed_record)

        if content:
            records = [
                record for record in records if record['content'] == content]

        LOGGER.debug('list_records: %s', records)
        return records

    # Create or update a record.
    def _update_record(self, identifier, rtype=None, name=None, content=None):

        data = {}

        if identifier is None:
            records = self._list_records(rtype, name, content)
            identifiers = [record["id"] for record in records]
        else:
            identifiers = [identifier]

        if name:
            data['name'] = self._relative_name(name)
        if content:
            data['content'] = content
        if self._get_lexicon_option('ttl'):
            data['ttl'] = self._get_lexicon_option('ttl')
        if self._get_lexicon_option('priority'):
            data['priority'] = self._get_lexicon_option('priority')
        if self._get_provider_option('regions'):
            data['regions'] = self._get_provider_option('regions')

        for one_identifier in identifiers:
            self._patch('/{0}/zones/{1}/records/{2}'
                        .format(self.account_id, self.domain, one_identifier), data)
            LOGGER.debug('update_record: %s', one_identifier)

        LOGGER.debug('update_record: %s', True)
        return True

    # Delete an existing record.
    # If record does not exist, do nothing.
    def _delete_record(self, identifier=None, rtype=None, name=None, content=None):
        delete_record_id = []
        if not identifier:
            records = self._list_records(rtype, name, content)
            delete_record_id = [record['id'] for record in records]
        else:
            delete_record_id.append(identifier)

        LOGGER.debug('delete_records: %s', delete_record_id)

        for record_id in delete_record_id:
            self._delete(
                '/{0}/zones/{1}/records/{2}'.format(self.account_id, self.domain, record_id))

        # is always True at this point; if a non 2xx response is returned, an error is raised.
        LOGGER.debug('delete_record: True')
        return True

    # Helpers

    def _request(self, action='GET', url='/', data=None, query_params=None):
        if data is None:
            data = {}
        if query_params is None:
            query_params = {}
        default_headers = {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        }
        default_auth = None

        if self._get_provider_option('auth_token'):
            default_headers['Authorization'] = "Bearer {0}".format(
                self._get_provider_option('auth_token'))
        elif (self._get_provider_option('auth_username')
              and self._get_provider_option('auth_password')):
            default_auth = (self._get_provider_option(
                'auth_username'), self._get_provider_option('auth_password'))
            if self._get_provider_option('auth_2fa'):
                default_headers['X-Dnsimple-OTP'] = self._get_provider_option(
                    'auth_2fa')
        else:
            raise Exception('No valid authentication mechanism found')

        response = requests.request(action, self.api_endpoint + url, params=query_params,
                                    data=json.dumps(data),
                                    headers=default_headers,
                                    auth=default_auth)
        # if the request fails for any reason, throw an error.
        response.raise_for_status()
        if response.text and response.json()['data'] is None:
            raise Exception('No data returned')

        return response.json()['data'] if response.text else None

    def _patch(self, url='/', data=None, query_params=None):
        return self._request('PATCH', url, data=data, query_params=query_params)
<|endoftext|>


<commit_before>from collections import OrderedDict
from flask import request, session, render_template, send_file, Blueprint
from natsort import natsorted
from lexos.helpers import constants as constants
from lexos.managers import utility, session_manager as session_manager
from lexos.models.filemanager_model import FileManagerModel
from lexos.models.topword_model import TopwordModel
from lexos.views.base_view import detect_active_docs

# this is a flask blue print
# it helps us to manage groups of views
# see here for more detail:
# http://exploreflask.com/en/latest/blueprints.html
# http://flask.pocoo.org/docs/0.12/blueprints/
top_words_blueprint = Blueprint('top_words', __name__)

# Tells Flask to load this function when someone is at '/topword'
@top_words_blueprint.route("/topword", methods=["GET"])
def top_words():
    """Handles the topword page functionality.

    :return: a response object (often a render_template call) to flask and
    eventually to the browser.
    """
    # Detect the number of active documents.
    num_active_docs = detect_active_docs()
    file_manager = utility.load_file_manager()
    labels = file_manager.get_active_labels_with_id()
    labels = OrderedDict(natsorted(list(labels.items()), key=lambda x: x[1]))

    # 'GET' request occurs when the page is first loaded
    if 'topwordoption' not in session:
        session['topwordoption'] = constants.DEFAULT_TOPWORD_OPTIONS
    if 'analyoption' not in session:
        session['analyoption'] = constants.DEFAULT_ANALYZE_OPTIONS

    # get the class division map and number of existing classes
    class_division_map = FileManagerModel().load_file_manager().\
        get_class_division_map()
    num_class = class_division_map.shape[0]
    return render_template(
        'topword.html',
        labels=labels,
        classmap=class_division_map,
        numclass=num_class,
        topwordsgenerated='class_div',
        itm='topwords',
        numActiveDocs=num_active_docs)

@top_words_blueprint.route("/topword", methods=["POST"])
def topword_html():
    # 'POST' request occurs when html form is submitted
    num_active_docs = detect_active_docs()
    file_manager = utility.load_file_manager()
    labels = file_manager.get_active_labels_with_id()
    labels = OrderedDict(natsorted(list(labels.items()), key=lambda x: x[1]))

    # get the class division map and number of existing classes
    class_division_map = FileManagerModel().load_file_manager().\
        get_class_division_map()
    num_class = class_division_map.shape[0]
    top_word_result = TopwordModel().get_result(class_division_map)
    result = top_word_result.result
    header = top_word_result.header
    if 'get-topword' in request.form:  # download topword
        path = utility.get_top_word_csv(result, csv_header=header)
        session_manager.cache_analysis_option()
        session_manager.cache_top_word_options()
        return send_file(
            path,
            attachment_filename=constants.TOPWORD_CSV_FILE_NAME,
            as_attachment=True)
    else:
        session_manager.cache_analysis_option()
        session_manager.cache_top_word_options()
        return render_template(
            'topword.html',
            result=result,
            labels=labels,
            header=header,
            numclass=num_class,
            topwordsgenerated='True',
            classmap=[],
            itm='topwords',
            numActiveDocs=num_active_docs)
<commit_msg>Update view<commit_after>from collections import OrderedDict
from flask import request, session, render_template, send_file, Blueprint
from natsort import natsorted
from lexos.helpers import constants as constants
from lexos.managers import utility, session_manager as session_manager
from lexos.models.filemanager_model import FileManagerModel
from lexos.models.topword_model import TopwordModel
from lexos.views.base_view import detect_active_docs

# this is a flask blue print
# it helps us to manage groups of views
# see here for more detail:
# http://exploreflask.com/en/latest/blueprints.html
# http://flask.pocoo.org/docs/0.12/blueprints/
top_words_blueprint = Blueprint('top_words', __name__)

# Tells Flask to load this function when someone is at '/topword'
@top_words_blueprint.route("/topword", methods=["GET"])
def top_words():
    """Handles the topword page functionality.

    :return: a response object (often a render_template call) to flask and
    eventually to the browser.
    """
    # Detect the number of active documents.
    num_active_docs = detect_active_docs()
    file_manager = utility.load_file_manager()
    labels = file_manager.get_active_labels_with_id()
    labels = OrderedDict(natsorted(list(labels.items()), key=lambda x: x[1]))

    # 'GET' request occurs when the page is first loaded
    if 'topwordoption' not in session:
        session['topwordoption'] = constants.DEFAULT_TOPWORD_OPTIONS
    if 'analyoption' not in session:
        session['analyoption'] = constants.DEFAULT_ANALYZE_OPTIONS

    # get the class division map and number of existing classes
    class_division_map = FileManagerModel().load_file_manager().\
        get_class_division_map()
    num_class = class_division_map.shape[0]
    return render_template(
        'topword.html',
        labels=labels,
        classmap=class_division_map,
        numclass=num_class,
        topwordsgenerated='class_div',
        itm='topwords',
        numActiveDocs=num_active_docs)

@top_words_blueprint.route("/topword", methods=["POST"])
def topword_html():
    # 'POST' request occurs when html form is submitted
    num_active_docs = detect_active_docs()
    file_manager = utility.load_file_manager()
    labels = file_manager.get_active_labels_with_id()
    labels = OrderedDict(natsorted(list(labels.items()), key=lambda x: x[1]))

    # get the class division map and number of existing classes
    class_division_map = FileManagerModel().load_file_manager().\
        get_class_division_map()
    num_class = class_division_map.shape[0]
    if 'get-topword' in request.form:  # download topword
        path = TopwordModel().get_top_word_csv(class_division_map)
        session_manager.cache_analysis_option()
        session_manager.cache_top_word_options()
        return send_file(
            path,
            attachment_filename=constants.TOPWORD_CSV_FILE_NAME,
            as_attachment=True)
    else:
        session_manager.cache_analysis_option()
        session_manager.cache_top_word_options()
        topword_result = TopwordModel().get_readable_result(class_division_map)
        return render_template(
            'topword.html',
            result=topword_result.results,
            labels=labels,
            header=topword_result.header,
            numclass=num_class,
            topwordsgenerated='True',
            classmap=[],
            itm='topwords',
            numActiveDocs=num_active_docs)
<|endoftext|>