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

import sys
import io
import os

import time
from time import sleep

import numpy
import matplotlib
from PIL import Image
from PyQt5.QtCore import *
import PyQt5.QtCore
from PyQt5.QtGui import *

from PyQt5.QtWidgets import *

from pyqt import FreakingQtImageViewer

from skimage import color
#from skimage import data
from skimage.feature import canny
from skimage.transform import hough_ellipse
from skimage.draw import ellipse_perimeter

from skimage.transform import hough_circle
from skimage.feature import peak_local_max
from skimage.draw import circle_perimeter
from skimage.util import img_as_ubyte

import scipy.misc

import picamera
import picamera.array

from cv import circle_detection

class DraughtsGameWindow(QWidget):
	def __init__(self):
		super().__init__()
		self.CELLS_PER_ROW = 8
		self.BLACK_COLOR = QColor(0,0,0,255)
		self.WHITE_COLOR = QColor(255,255,255,255)
		self.BLUE_COLOR = QColor(0,0,255,255)
		self.RED_COLOR = QColor(255,0,0,255)
		self.GREEN_COLOR = QColor(0,255,0,255)
		self.DEFAULT_PEN = QPen(self.BLUE_COLOR)
		self.DEFAULT_PEN.setWidth(20)
		self.PIX_PADDING = 75
		self.BORDER_RADIUS = 10

		self.initUI()

	def keyPressEvent(self, event):
		if event.key() == Qt.Key_Escape:
			self.close()
		elif event.key() == Qt.Key_C:
			self.capture()

	def initUI(self):

		self.WIDTH, self.HEIGHT, self.CELL_SIZE = screen_rect.width(), screen_rect.height(), screen_rect.width() / self.CELLS_PER_ROW
		self.pixmap = QPixmap(self.WIDTH-20,self.HEIGHT-20)

		self.upper_left = (self.PIX_PADDING + self.WIDTH / 2 - self.pixmap.height() / 2, self.PIX_PADDING)
		self.upper_right = (- self.PIX_PADDING + self.WIDTH / 2 + self.pixmap.height() / 2, self.PIX_PADDING + 0)
		self.lower_right = (-self.PIX_PADDING + self.WIDTH / 2 + self.pixmap.height() / 2, -self.PIX_PADDING + self.pixmap.height())
		self.lower_left = (self.PIX_PADDING + self.WIDTH / 2 - self.pixmap.height() / 2, -self.PIX_PADDING + self.pixmap.height())

		self.DRAW_SIZE = self.upper_right[0] - self.upper_left[0]
		print(self.DRAW_SIZE)

		self.painter = QPainter(self.pixmap)
		self.painter.setPen(self.DEFAULT_PEN)

		self.painter.fillRect(0, 0,self.WIDTH, self.HEIGHT, self.WHITE_COLOR)
		self.painter.drawEllipse(self.upper_left[0]-10,self.upper_left[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.BLACK_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.upper_right[0]-10,self.upper_right[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.RED_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.lower_right[0]-10,self.lower_right[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.GREEN_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.lower_left[0]-10,self.lower_left[1]-10,20,20)

		self.vbox = QVBoxLayout(self)

		self.lbl = QLabel(self)
		self.lbl.setPixmap(self.pixmap)
		self.vbox.addWidget(self.lbl)

		self.setLayout(self.vbox)
		self.vbox.setAlignment(Qt.AlignHCenter)
		self.showFullScreen()
		self.setWindowTitle('DraughtsCV')
		self.show()
		#capture()

	def checkCoords(self, img, coords):
		self.newCoords = []
		self.redC = []
		self.greenC = []
		self.blueC = []
		self.blackC = []
		for index, coord in enumerate(coords):
			#print(img[coord[0], coord[1]]," - ", )
			pixel = img[coord[0], coord[1]]
			sum = 0
			sum += pixel[0]
			sum += pixel[1]
			sum += pixel[2]
			if sum > 300:
				print("delete ", coord, ' with ', pixel)
			else:
				self.newCoords.append(coord)

		for coord in self.newCoords:
			pixel = img[coord[0], coord[1]]

			if pixel[0] < 50 and pixel[1] < 50 and pixel[2] < 50:
				print(coord, ' could be black with', pixel)
				self.blackC.append(coord)
				self.DEFAULT_PEN.setColor(self.BLACK_COLOR)
				self.painter.setPen(self.DEFAULT_PEN)
				self.painter.drawEllipse(coord[1], coord[0], self.BORDER_RADIUS,self.BORDER_RADIUS)
			elif pixel[0] > 110:
				print(coord, ' could be red with', pixel)
				self.redC.append(coord)
				self.DEFAULT_PEN.setColor(self.RED_COLOR)
				self.painter.setPen(self.DEFAULT_PEN)
				self.painter.drawEllipse(coord[1], coord[0], self.BORDER_RADIUS,self.BORDER_RADIUS)
			elif pixel[1] > 110:
				print(coord, ' could be green with', pixel)
				self.redC.append(coord)
				self.DEFAULT_PEN.setColor(self.GREEN_COLOR)
				self.painter.setPen(self.DEFAULT_PEN)
				self.painter.drawEllipse(coord[1], coord[0], self.BORDER_RADIUS,self.BORDER_RADIUS)
			elif pixel[2] > 110:
				print(coord, ' could be blue with', pixel)
				self.blueC.append(coord)
				self.DEFAULT_PEN.setColor(self.BLUE_COLOR)
				self.painter.setPen(self.DEFAULT_PEN)
				self.painter.drawEllipse(coord[1], coord[0], self.BORDER_RADIUS,self.BORDER_RADIUS)

		#final_red = (int(numpy.sum(self.redC[0])/len(self.redC)),int(numpy.sum(self.redC[1])/len(self.redC[])))	#(sum(self.redC[0]), sum(self.redC[1]))
		final_red = (int(numpy.sum(self.redC[0])),int(numpy.sum(self.redC[1])))
		print(final_red)

		self.lbl.setPixmap(self.pixmap)

	def capture(self):
		camera = picamera.PiCamera()
		with picamera.array.PiRGBArray(camera) as stream:
			camera.capture(stream, format='rgb')
			img = stream.array
			img, coords = circle_detection(img, 20, 25)
			im = Image.fromarray(img) #.convert('LA')
			im.save('./tmp.png')
			camera.close()
			self.checkCoords(img, coords)

if __name__ == '__main__':

	app = QApplication(sys.argv)

	screen_rect = app.desktop().screenGeometry()
	viewer = DraughtsGameWindow() # init ui
	# capture
	sys.exit(app.exec_())<commit_msg>plswork<commit_after>#!/usr/bin/env python3

import sys
import io
import os

import time
from time import sleep

import numpy
import matplotlib
from PIL import Image
from PyQt5.QtCore import *
import PyQt5.QtCore
from PyQt5.QtGui import *

from PyQt5.QtWidgets import *

from pyqt import FreakingQtImageViewer

from skimage import color
#from skimage import data
from skimage.feature import canny
from skimage.transform import hough_ellipse
from skimage.draw import ellipse_perimeter

from skimage.transform import hough_circle
from skimage.feature import peak_local_max
from skimage.draw import circle_perimeter
from skimage.util import img_as_ubyte

import scipy.misc

import picamera
import picamera.array

from cv import circle_detection

class DraughtsGameWindow(QWidget):
	def __init__(self):
		super().__init__()
		self.CELLS_PER_ROW = 8
		self.BLACK_COLOR = QColor(0,0,0,255)
		self.WHITE_COLOR = QColor(255,255,255,255)
		self.BLUE_COLOR = QColor(0,0,255,255)
		self.RED_COLOR = QColor(255,0,0,255)
		self.GREEN_COLOR = QColor(0,255,0,255)
		self.DEFAULT_PEN = QPen(self.BLUE_COLOR)
		self.DEFAULT_PEN.setWidth(20)
		self.PIX_PADDING = 75
		self.BORDER_RADIUS = 10

		self.initUI()

	def keyPressEvent(self, event):
		if event.key() == Qt.Key_Escape:
			self.close()
		elif event.key() == Qt.Key_C:
			self.capture()

	def initUI(self):

		self.WIDTH, self.HEIGHT, self.CELL_SIZE = screen_rect.width(), screen_rect.height(), screen_rect.width() / self.CELLS_PER_ROW
		self.pixmap = QPixmap(self.WIDTH-20,self.HEIGHT-20)

		self.upper_left = (self.PIX_PADDING + self.WIDTH / 2 - self.pixmap.height() / 2, self.PIX_PADDING)
		self.upper_right = (- self.PIX_PADDING + self.WIDTH / 2 + self.pixmap.height() / 2, self.PIX_PADDING + 0)
		self.lower_right = (-self.PIX_PADDING + self.WIDTH / 2 + self.pixmap.height() / 2, -self.PIX_PADDING + self.pixmap.height())
		self.lower_left = (self.PIX_PADDING + self.WIDTH / 2 - self.pixmap.height() / 2, -self.PIX_PADDING + self.pixmap.height())

		self.DRAW_SIZE = self.upper_right[0] - self.upper_left[0]
		print(self.DRAW_SIZE)

		self.painter = QPainter(self.pixmap)
		self.painter.setPen(self.DEFAULT_PEN)

		self.painter.fillRect(0, 0,self.WIDTH, self.HEIGHT, self.WHITE_COLOR)
		self.painter.drawEllipse(self.upper_left[0]-10,self.upper_left[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.BLACK_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.upper_right[0]-10,self.upper_right[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.RED_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.lower_right[0]-10,self.lower_right[1]-10,20,20)
		self.DEFAULT_PEN.setColor(self.GREEN_COLOR)
		self.painter.setPen(self.DEFAULT_PEN)
		self.painter.drawEllipse(self.lower_left[0]-10,self.lower_left[1]-10,20,20)

		self.vbox = QVBoxLayout(self)

		self.lbl = QLabel(self)
		self.lbl.setPixmap(self.pixmap)
		self.vbox.addWidget(self.lbl)

		self.setLayout(self.vbox)
		self.vbox.setAlignment(Qt.AlignHCenter)
		self.showFullScreen()
		self.setWindowTitle('DraughtsCV')
		self.show()
		#capture()

	def checkCoords(self, img, coords):
		self.newCoords = []
		self.redC = []
		self.greenC = []
		self.blueC = []
		self.blackC = []
		for index, coord in enumerate(coords):
			#print(img[coord[0], coord[1]]," - ", )
			pixel = img[coord[0], coord[1]]
			sum = 0
			sum += pixel[0]
			sum += pixel[1]
			sum += pixel[2]
			if sum > 300:
				print("delete ", coord, ' with ', pixel)
			else:
				self.newCoords.append(coord)

		for coord in self.newCoords:
			pixel = img[coord[0], coord[1]]

			if pixel[0] < 50 and pixel[1] < 50 and pixel[2] < 50:
				print(coord, ' could be black with', pixel)
				self.blackC.append(coord)
				self.DEFAULT_PEN.setColor(self.BLACK_COLOR)
				self.painter.setPen(self.DEFAULT_PEN)
				self.painter.drawEllipse(coord[1], coord[0], self.BORDER_RADIUS,self.BORDER_RADIUS)
			elif pixel[0] > 110:
				print(coord, ' could be red with', pixel)
				self.redC.append(coord)
				self.DEFAULT_PEN.setColor(self.RED_COLOR)
				self.painter.setPen(self.DEFAULT_PEN)
				self.painter.drawEllipse(coord[1], coord[0], self.BORDER_RADIUS,self.BORDER_RADIUS)
			elif pixel[1] > 110:
				print(coord, ' could be green with', pixel)
				self.greenC.append(coord)
				self.DEFAULT_PEN.setColor(self.GREEN_COLOR)
				self.painter.setPen(self.DEFAULT_PEN)
				self.painter.drawEllipse(coord[1], coord[0], self.BORDER_RADIUS,self.BORDER_RADIUS)
			elif pixel[2] > 110:
				print(coord, ' could be blue with', pixel)
				self.blueC.append(coord)
				self.DEFAULT_PEN.setColor(self.BLUE_COLOR)
				self.painter.setPen(self.DEFAULT_PEN)
				self.painter.drawEllipse(coord[1], coord[0], self.BORDER_RADIUS,self.BORDER_RADIUS)

		#final_red = (int(numpy.sum(self.redC[0])/len(self.redC)),int(numpy.sum(self.redC[1])/len(self.redC[])))	#(sum(self.redC[0]), sum(self.redC[1]))
		final_red = (int(numpy.sum(self.redC[0])),int(numpy.sum(self.redC[1])))
		print(final_red)

		self.lbl.setPixmap(self.pixmap)

	def capture(self):
		camera = picamera.PiCamera()
		with picamera.array.PiRGBArray(camera) as stream:
			camera.capture(stream, format='rgb')
			img = stream.array
			img, coords = circle_detection(img, 20, 25)
			im = Image.fromarray(img) #.convert('LA')
			im.save('./tmp.png')
			camera.close()
			self.checkCoords(img, coords)

if __name__ == '__main__':

	app = QApplication(sys.argv)

	screen_rect = app.desktop().screenGeometry()
	viewer = DraughtsGameWindow() # init ui
	# capture
	sys.exit(app.exec_())<|endoftext|>


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

import operator
import re
from typing import Callable, Dict  # noqa: H301

import pyparsing

from cinder import exception
from cinder.i18n import _

def _operatorOperands(tokenList):
    it = iter(tokenList)
    while True:
        try:
            op1 = next(it)
            op2 = next(it)
            yield(op1, op2)
        except StopIteration:
            break

class EvalConstant(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        result = self.value
        if (isinstance(result, str) and
                re.match(r"^[a-zA-Z_]+\.[a-zA-Z_]+$", result)):
            (which_dict, entry) = result.split('.')
            try:
                result = _vars[which_dict][entry]
            except KeyError:
                raise exception.EvaluatorParseException(
                    _("KeyError evaluating string"))
            except TypeError:
                raise exception.EvaluatorParseException(
                    _("TypeError evaluating string"))

        try:
            result = int(result)
        except ValueError:
            try:
                result = float(result)
            except ValueError:
                if isinstance(result, str):
                    result = result.replace('"', '').replace('\'', '')

        return result

class EvalSignOp(object):
    operations = {
        '+': 1,
        '-': -1,
    }

    def __init__(self, toks):
        self.sign, self.value = toks[0]

    def eval(self):
        return self.operations[self.sign] * self.value.eval()

class EvalAddOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        sum = self.value[0].eval()
        for op, val in _operatorOperands(self.value[1:]):
            if op == '+':
                sum += val.eval()
            elif op == '-':
                sum -= val.eval()
        return sum

class EvalMultOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        prod = self.value[0].eval()
        for op, val in _operatorOperands(self.value[1:]):
            try:
                if op == '*':
                    prod *= val.eval()
                elif op == '/':
                    prod /= float(val.eval())
            except ZeroDivisionError as e:
                raise exception.EvaluatorParseException(
                    _("ZeroDivisionError: %s") % e)
        return prod

class EvalPowerOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        prod = self.value[0].eval()
        for op, val in _operatorOperands(self.value[1:]):
            prod = pow(prod, val.eval())
        return prod

class EvalNegateOp(object):
    def __init__(self, toks):
        self.negation, self.value = toks[0]

    def eval(self):
        return not self.value.eval()

class EvalComparisonOp(object):
    operations = {
        "<": operator.lt,
        "<=": operator.le,
        ">": operator.gt,
        ">=": operator.ge,
        "!=": operator.ne,
        "==": operator.eq,
        "<>": operator.ne,
    }

    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        val1 = self.value[0].eval()
        for op, val in _operatorOperands(self.value[1:]):
            fn = self.operations[op]
            val2 = val.eval()
            if not fn(val1, val2):
                break
            val1 = val2
        else:
            return True
        return False

class EvalTernaryOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        condition = self.value[0].eval()
        if condition:
            return self.value[2].eval()
        else:
            return self.value[4].eval()

class EvalFunction(object):
    functions: Dict[str, Callable] = {
        "abs": abs,
        "max": max,
        "min": min,
    }

    def __init__(self, toks):
        self.func, self.value = toks[0]

    def eval(self):
        args = self.value.eval()
        if type(args) is list:
            return self.functions[self.func](*args)
        else:
            return self.functions[self.func](args)

class EvalCommaSeperator(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        val1 = self.value[0].eval()
        val2 = self.value[2].eval()
        if type(val2) is list:
            val_list = []
            val_list.append(val1)
            for val in val2:
                val_list.append(val)
            return val_list

        return [val1, val2]

class EvalBoolAndOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        left = self.value[0].eval()
        right = self.value[2].eval()
        return left and right

class EvalBoolOrOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        left = self.value[0].eval()
        right = self.value[2].eval()
        return left or right

_parser = None
_vars = {}

def _def_parser():
    # Enabling packrat parsing greatly speeds up the parsing.
    pyparsing.ParserElement.enablePackrat()

    alphas = pyparsing.alphas
    Combine = pyparsing.Combine
    nums = pyparsing.nums
    quoted_string = pyparsing.quotedString

    oneOf = pyparsing.oneOf
    opAssoc = pyparsing.opAssoc
    operatorPrecedence = pyparsing.operatorPrecedence
    Word = pyparsing.Word

    integer = Word(nums)
    real = Combine(Word(nums) + '.' + Word(nums))
    variable = Word(alphas + '_' + '.')
    number = real | integer
    fn = Word(alphas + '_' + '.')
    operand = number | variable | fn | quoted_string

    signop = oneOf('+ -')
    addop = oneOf('+ -')
    multop = oneOf('* /')
    comparisonop = oneOf(' '.join(EvalComparisonOp.operations.keys()))
    ternaryop = ('?', ':')
    boolandop = oneOf('AND and &&')
    boolorop = oneOf('OR or ||')
    negateop = oneOf('NOT not !')

    operand.setParseAction(EvalConstant)
    expr = operatorPrecedence(operand, [
        (fn, 1, opAssoc.RIGHT, EvalFunction),
        ("^", 2, opAssoc.RIGHT, EvalPowerOp),
        (signop, 1, opAssoc.RIGHT, EvalSignOp),
        (multop, 2, opAssoc.LEFT, EvalMultOp),
        (addop, 2, opAssoc.LEFT, EvalAddOp),
        (negateop, 1, opAssoc.RIGHT, EvalNegateOp),
        (comparisonop, 2, opAssoc.LEFT, EvalComparisonOp),
        (ternaryop, 3, opAssoc.LEFT, EvalTernaryOp),
        (boolandop, 2, opAssoc.LEFT, EvalBoolAndOp),
        (boolorop, 2, opAssoc.LEFT, EvalBoolOrOp),
        (',', 2, opAssoc.RIGHT, EvalCommaSeperator), ])

    return expr

def evaluate(expression, **kwargs):
    """Evaluates an expression.

    Provides the facility to evaluate mathematical expressions, and to
    substitute variables from dictionaries into those expressions.

    Supports both integer and floating point values, and automatic
    promotion where necessary.
    """
    global _parser
    if _parser is None:
        _parser = _def_parser()

    global _vars
    _vars = kwargs

    try:
        result = _parser.parseString(expression, parseAll=True)[0]
    except pyparsing.ParseException as e:
        raise exception.EvaluatorParseException(
            _("ParseException: %s") % e)

    return result.eval()
<commit_msg>replace deprecated pyparsing method<commit_after># Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import operator
import re
from typing import Callable, Dict  # noqa: H301

import pyparsing

from cinder import exception
from cinder.i18n import _

def _operatorOperands(tokenList):
    it = iter(tokenList)
    while True:
        try:
            op1 = next(it)
            op2 = next(it)
            yield(op1, op2)
        except StopIteration:
            break

class EvalConstant(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        result = self.value
        if (isinstance(result, str) and
                re.match(r"^[a-zA-Z_]+\.[a-zA-Z_]+$", result)):
            (which_dict, entry) = result.split('.')
            try:
                result = _vars[which_dict][entry]
            except KeyError:
                raise exception.EvaluatorParseException(
                    _("KeyError evaluating string"))
            except TypeError:
                raise exception.EvaluatorParseException(
                    _("TypeError evaluating string"))

        try:
            result = int(result)
        except ValueError:
            try:
                result = float(result)
            except ValueError:
                if isinstance(result, str):
                    result = result.replace('"', '').replace('\'', '')

        return result

class EvalSignOp(object):
    operations = {
        '+': 1,
        '-': -1,
    }

    def __init__(self, toks):
        self.sign, self.value = toks[0]

    def eval(self):
        return self.operations[self.sign] * self.value.eval()

class EvalAddOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        sum = self.value[0].eval()
        for op, val in _operatorOperands(self.value[1:]):
            if op == '+':
                sum += val.eval()
            elif op == '-':
                sum -= val.eval()
        return sum

class EvalMultOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        prod = self.value[0].eval()
        for op, val in _operatorOperands(self.value[1:]):
            try:
                if op == '*':
                    prod *= val.eval()
                elif op == '/':
                    prod /= float(val.eval())
            except ZeroDivisionError as e:
                raise exception.EvaluatorParseException(
                    _("ZeroDivisionError: %s") % e)
        return prod

class EvalPowerOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        prod = self.value[0].eval()
        for op, val in _operatorOperands(self.value[1:]):
            prod = pow(prod, val.eval())
        return prod

class EvalNegateOp(object):
    def __init__(self, toks):
        self.negation, self.value = toks[0]

    def eval(self):
        return not self.value.eval()

class EvalComparisonOp(object):
    operations = {
        "<": operator.lt,
        "<=": operator.le,
        ">": operator.gt,
        ">=": operator.ge,
        "!=": operator.ne,
        "==": operator.eq,
        "<>": operator.ne,
    }

    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        val1 = self.value[0].eval()
        for op, val in _operatorOperands(self.value[1:]):
            fn = self.operations[op]
            val2 = val.eval()
            if not fn(val1, val2):
                break
            val1 = val2
        else:
            return True
        return False

class EvalTernaryOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        condition = self.value[0].eval()
        if condition:
            return self.value[2].eval()
        else:
            return self.value[4].eval()

class EvalFunction(object):
    functions: Dict[str, Callable] = {
        "abs": abs,
        "max": max,
        "min": min,
    }

    def __init__(self, toks):
        self.func, self.value = toks[0]

    def eval(self):
        args = self.value.eval()
        if type(args) is list:
            return self.functions[self.func](*args)
        else:
            return self.functions[self.func](args)

class EvalCommaSeperator(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        val1 = self.value[0].eval()
        val2 = self.value[2].eval()
        if type(val2) is list:
            val_list = []
            val_list.append(val1)
            for val in val2:
                val_list.append(val)
            return val_list

        return [val1, val2]

class EvalBoolAndOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        left = self.value[0].eval()
        right = self.value[2].eval()
        return left and right

class EvalBoolOrOp(object):
    def __init__(self, toks):
        self.value = toks[0]

    def eval(self):
        left = self.value[0].eval()
        right = self.value[2].eval()
        return left or right

_parser = None
_vars = {}

def _def_parser():
    # Enabling packrat parsing greatly speeds up the parsing.
    pyparsing.ParserElement.enablePackrat()

    alphas = pyparsing.alphas
    Combine = pyparsing.Combine
    nums = pyparsing.nums
    quoted_string = pyparsing.quotedString

    oneOf = pyparsing.oneOf
    opAssoc = pyparsing.opAssoc
    infixNotation = pyparsing.infixNotation
    Word = pyparsing.Word

    integer = Word(nums)
    real = Combine(Word(nums) + '.' + Word(nums))
    variable = Word(alphas + '_' + '.')
    number = real | integer
    fn = Word(alphas + '_' + '.')
    operand = number | variable | fn | quoted_string

    signop = oneOf('+ -')
    addop = oneOf('+ -')
    multop = oneOf('* /')
    comparisonop = oneOf(' '.join(EvalComparisonOp.operations.keys()))
    ternaryop = ('?', ':')
    boolandop = oneOf('AND and &&')
    boolorop = oneOf('OR or ||')
    negateop = oneOf('NOT not !')

    operand.setParseAction(EvalConstant)
    expr = infixNotation(operand, [
        (fn, 1, opAssoc.RIGHT, EvalFunction),
        ("^", 2, opAssoc.RIGHT, EvalPowerOp),
        (signop, 1, opAssoc.RIGHT, EvalSignOp),
        (multop, 2, opAssoc.LEFT, EvalMultOp),
        (addop, 2, opAssoc.LEFT, EvalAddOp),
        (negateop, 1, opAssoc.RIGHT, EvalNegateOp),
        (comparisonop, 2, opAssoc.LEFT, EvalComparisonOp),
        (ternaryop, 3, opAssoc.LEFT, EvalTernaryOp),
        (boolandop, 2, opAssoc.LEFT, EvalBoolAndOp),
        (boolorop, 2, opAssoc.LEFT, EvalBoolOrOp),
        (',', 2, opAssoc.RIGHT, EvalCommaSeperator), ])

    return expr

def evaluate(expression, **kwargs):
    """Evaluates an expression.

    Provides the facility to evaluate mathematical expressions, and to
    substitute variables from dictionaries into those expressions.

    Supports both integer and floating point values, and automatic
    promotion where necessary.
    """
    global _parser
    if _parser is None:
        _parser = _def_parser()

    global _vars
    _vars = kwargs

    try:
        result = _parser.parseString(expression, parseAll=True)[0]
    except pyparsing.ParseException as e:
        raise exception.EvaluatorParseException(
            _("ParseException: %s") % e)

    return result.eval()
<|endoftext|>


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

from hashlib import md5
from math import isnan
from datetime import datetime
from flask import current_app
from importlib import import_module
from fastnumbers import isfloat
from flask_mongoengine import DynamicDocument
from mongoengine import CASCADE, signals
from mongoengine.queryset import DoesNotExist
from mongoengine.fields import StringField, BooleanField, DictField
from mongoengine.fields import LazyReferenceField, ReferenceField
from mongoengine.fields import DateTimeField, ListField
from marshmallow.utils import get_value, _Missing
from boltons.iterutils import remap
from decimal import Decimal
from pint import UnitRegistry
from pint.unit import UnitDefinition
from pint.converters import ScaleConverter
from pint.errors import DimensionalityError
from uncertainties import ufloat_fromstr
from collections import defaultdict

from mpcontribs.api import enter, valid_dict, delimiter

quantity_keys = {"display", "value", "error", "unit"}
max_dgts = 6
ureg = UnitRegistry(
    autoconvert_offset_to_baseunit=True,
    preprocessors=[
        lambda s: s.replace("%%", " permille "),
        lambda s: s.replace("%", " percent "),
    ],
)
ureg.default_format = ",P~"

ureg.define(UnitDefinition("percent", "%", (), ScaleConverter(0.01)))
ureg.define(UnitDefinition("permille", "%%", (), ScaleConverter(0.001)))
ureg.define(UnitDefinition("ppm", "ppm", (), ScaleConverter(1e-6)))
ureg.define(UnitDefinition("ppb", "ppb", (), ScaleConverter(1e-9)))
ureg.define("atom = 1")
ureg.define("bohr_magneton = e * hbar / (2 * m_e) = µᵇ = µ_B = mu_B")
ureg.define("electron_mass = 9.1093837015e-31 kg = mₑ = m_e")

COMPONENTS = {
    "structures": ["lattice", "sites", "charge"],
    "tables": ["index", "columns", "data"],
    "attachments": ["mime", "content"],
}

def format_cell(cell):
    if cell.count(" ") > 1:
        return cell

    q = get_quantity(cell)
    if not q:
        return cell

    q = truncate_digits(q)
    return str(q.value) if isnan(q.std_dev) else str(q)

def new_error_units(measurement, quantity):
    if quantity.units == measurement.value.units:
        return measurement

    error = measurement.error.to(quantity.units)
    return ureg.Measurement(quantity, error)

def get_quantity(s):
    # 5, 5 eV, 5+/-1 eV, 5(1) eV
    # set uncertainty to nan if not provided
    parts = s.split()
    parts += [None] * (2 - len(parts))
    if isfloat(parts[0]):
        parts[0] += "+/-nan"

    try:
        parts[0] = ufloat_fromstr(parts[0])
        return ureg.Measurement(*parts)
    except ValueError:
        return None

def truncate_digits(q):
    if isnan(q.nominal_value):
        return q

    v = Decimal(str(q.nominal_value))
    vt = v.as_tuple()

    if vt.exponent >= 0:
        return q

    dgts = len(vt.digits)
    dgts = max_dgts if dgts > max_dgts else dgts
    s = f"{v:.{dgts}g}"
    if not isnan(q.std_dev):
        s += f"+/-{q.std_dev:.{dgts}g}"

    if q.units:
        s += f" {q.units}"

    return get_quantity(s)

def get_min_max(sender, path):
    # NOTE can't filter for project when using wildcard index data.$**
    # https://docs.mongodb.com/manual/core/index-wildcard/#wildcard-index-query-sort-support
    field = f"{path}{delimiter}value"
    key = f"{field}__type".replace(delimiter, "__")
    q = {key: "number"}  # NOTE need a query to trigger wildcard IXSCAN
    qs = sender.objects(**q).only(field).order_by(field)
    values = [get_value(doc, field) for doc in qs]
    values = [v for v in values if not isinstance(v, _Missing)]
    return (values[0], values[-1]) if len(values) else (None, None)

def get_resource(component):
    klass = component.capitalize()
    vmodule = import_module(f"mpcontribs.api.{component}.views")
    Resource = getattr(vmodule, f"{klass}Resource")
    return Resource()

def get_md5(resource, obj, fields):
    d = resource.serialize(obj, fields=fields)
    s = json.dumps(d, sort_keys=True).encode("utf-8")
    return md5(s).hexdigest()

class Contributions(DynamicDocument):
    project = LazyReferenceField(
        "Projects", required=True, passthrough=True, reverse_delete_rule=CASCADE
    )
    identifier = StringField(required=True, help_text="material/composition identifier")
    formula = StringField(help_text="formula (set dynamically if not provided)")
    is_public = BooleanField(
        required=True, default=False, help_text="public/private contribution"
    )
    last_modified = DateTimeField(
        required=True, default=datetime.utcnow, help_text="time of last modification"
    )
    data = DictField(
        default={}, validation=valid_dict, help_text="simple free-form data"
    )
    structures = ListField(
        ReferenceField("Structures", null=True), default=list, max_length=10
    )
    tables = ListField(
        ReferenceField("Tables", null=True), default=list, max_length=10
    )
    attachments = ListField(
        ReferenceField("Attachments", null=True), default=list, max_length=10
    )
    notebook = LazyReferenceField("Notebooks", passthrough=True)
    meta = {
        "collection": "contributions",
        "indexes": [
            "project",
            "identifier",
            "formula",
            "is_public",
            "last_modified",
            {"fields": [(r"data.$**", 1)]},
            "notebook",
        ]
        + list(COMPONENTS.keys()),
    }

    @classmethod
    def post_init(cls, sender, document, **kwargs):
        # replace existing components with according ObjectIds
        for component, fields in COMPONENTS.items():
            lst = document._data.get(component)
            if lst and lst[0].id is None:  # id is None for incoming POST
                resource = get_resource(component)
                for i, o in enumerate(lst):
                    digest = get_md5(resource, o, fields)
                    obj = resource.document.objects(md5=digest).only("id").first()
                    if obj:
                        lst[i] = obj.to_dbref()

    @classmethod
    def pre_save_post_validation(cls, sender, document, **kwargs):
        if kwargs.get("skip"):
            return

        # set formula field
        if hasattr(document, "formula") and not document.formula:
            formulae = current_app.config["FORMULAE"]
            document.formula = formulae.get(document.identifier, document.identifier)

        # project is LazyReferenceField
        project = document.project.fetch()

        # run data through Pint Quantities and save as dicts
        def make_quantities(path, key, value):
            if key in quantity_keys or not isinstance(value, (str, int, float)):
                return key, value

            str_value = str(value)
            if str_value.count(" ") > 1:
                return key, value

            q = get_quantity(str_value)
            if not q:
                return key, value

            # silently ignore "nan"
            if isnan(q.nominal_value):
                return False

            # try compact representation
            qq = q.value.to_compact()
            q = new_error_units(q, qq)

            # reduce dimensionality if possible
            if not q.check(0):
                qq = q.value.to_reduced_units()
                q = new_error_units(q, qq)

            # ensure that the same units are used across contributions
            field = delimiter.join(["data"] + list(path) + [key])
            try:
                column = project.columns.get(path=field)
                if column.unit != str(q.value.units):
                    qq = q.value.to(column.unit)
                    q = new_error_units(q, qq)
            except DoesNotExist:
                pass  # column doesn't exist yet (generated in post_save)
            except DimensionalityError:
                raise ValueError(
                    f"Can't convert [{q.units}] to [{column.unit}] for {field}!"
                )

            # significant digits
            q = truncate_digits(q)

            # return new value dict
            display = str(q.value) if isnan(q.std_dev) else str(q)
            value = {
                "display": display,
                "value": q.nominal_value,
                "error": q.std_dev,
                "unit": str(q.units),
            }
            return key, value

        document.data = remap(document.data, visit=make_quantities, enter=enter)
        document.last_modified = datetime.utcnow()

    @classmethod
    def post_save(cls, sender, document, **kwargs):
        if kwargs.get("skip"):
            return

        # project is LazyReferenceField
        project = document.project.fetch()

        # set columns field for project
        def update_columns(path, key, value):
            path = delimiter.join(["data"] + list(path) + [key])
            is_quantity = isinstance(value, dict) and quantity_keys.issubset(
                value.keys()
            )
            is_text = bool(
                not is_quantity and isinstance(value, str) and key not in quantity_keys
            )
            if is_quantity or is_text:
                project.reload("columns")
                try:
                    column = project.columns.get(path=path)
                    if is_quantity:
                        v = value["value"]
                        if isnan(column.max) or v > column.max:
                            column.max = v
                        if isnan(column.min) or v < column.min:
                            column.min = v

                except DoesNotExist:
                    column = {"path": path}
                    if is_quantity:
                        column["unit"] = value["unit"]
                        column["min"] = column["max"] = value["value"]

                    project.columns.create(**column)

                project.save().reload("columns")
                ncolumns = len(project.columns)
                if ncolumns > 50:
                    raise ValueError("Reached maximum number of columns (50)!")

            return True

        # run update_columns over document data
        remap(document.data, visit=update_columns, enter=enter)

        # add/remove columns for other components
        for path in COMPONENTS.keys():
            try:
                project.columns.get(path=path)
            except DoesNotExist:
                if getattr(document, path):
                    project.columns.create(path=path)
                    project.save().reload("columns")

    @classmethod
    def pre_delete(cls, sender, document, **kwargs):
        args = ["notebook"] + list(COMPONENTS.keys())
        document.reload(*args)
        deleted = defaultdict(list)

        for component in COMPONENTS.keys():
            # check if other contributions exist before deletion!
            for idx, obj in enumerate(getattr(document, component)):
                q = {component: obj.id}
                if sender.objects(**q).count() < 2:
                    obj.delete()
                    deleted[component].append(idx)

        # remove reference documents
        if document.notebook is not None:
            from mpcontribs.api.notebooks.document import Notebooks

            nid = document.notebook.id
            nb = Notebooks.objects(id=nid).first()
            nb.delete(signal_kwargs=deleted)

    @classmethod
    def post_delete(cls, sender, document, **kwargs):
        if kwargs.get("skip"):
            return

        # reset columns field for project
        project = document.project.fetch()

        for column in list(project.columns):
            if not isnan(column.min) and not isnan(column.max):
                column.min, column.max = get_min_max(sender, column.path)
                if isnan(column.min) and isnan(column.max):
                    # just deleted last contribution with this column
                    project.update(pull__columns__path=column.path)
            else:
                # use wildcard index if available -> single field query
                field = column.path.replace(delimiter, "__") + "__type"
                qs = sender.objects(**{field: "string"}).only(column.path)

                if qs.count() < 1 or qs.filter(project__name=project.name).count() < 1:
                    project.update(pull__columns__path=column.path)

signals.post_init.connect(Contributions.post_init, sender=Contributions)
signals.pre_save_post_validation.connect(
    Contributions.pre_save_post_validation, sender=Contributions
)
signals.post_save.connect(Contributions.post_save, sender=Contributions)
signals.pre_delete.connect(Contributions.pre_delete, sender=Contributions)
signals.post_delete.connect(Contributions.post_delete, sender=Contributions)
<commit_msg>api: fix format_cell for empty strings, use nominal_value<commit_after># -*- coding: utf-8 -*-
import json

from hashlib import md5
from math import isnan
from datetime import datetime
from flask import current_app
from importlib import import_module
from fastnumbers import isfloat
from flask_mongoengine import DynamicDocument
from mongoengine import CASCADE, signals
from mongoengine.queryset import DoesNotExist
from mongoengine.fields import StringField, BooleanField, DictField
from mongoengine.fields import LazyReferenceField, ReferenceField
from mongoengine.fields import DateTimeField, ListField
from marshmallow.utils import get_value, _Missing
from boltons.iterutils import remap
from decimal import Decimal
from pint import UnitRegistry
from pint.unit import UnitDefinition
from pint.converters import ScaleConverter
from pint.errors import DimensionalityError
from uncertainties import ufloat_fromstr
from collections import defaultdict

from mpcontribs.api import enter, valid_dict, delimiter

quantity_keys = {"display", "value", "error", "unit"}
max_dgts = 6
ureg = UnitRegistry(
    autoconvert_offset_to_baseunit=True,
    preprocessors=[
        lambda s: s.replace("%%", " permille "),
        lambda s: s.replace("%", " percent "),
    ],
)
ureg.default_format = ",P~"

ureg.define(UnitDefinition("percent", "%", (), ScaleConverter(0.01)))
ureg.define(UnitDefinition("permille", "%%", (), ScaleConverter(0.001)))
ureg.define(UnitDefinition("ppm", "ppm", (), ScaleConverter(1e-6)))
ureg.define(UnitDefinition("ppb", "ppb", (), ScaleConverter(1e-9)))
ureg.define("atom = 1")
ureg.define("bohr_magneton = e * hbar / (2 * m_e) = µᵇ = µ_B = mu_B")
ureg.define("electron_mass = 9.1093837015e-31 kg = mₑ = m_e")

COMPONENTS = {
    "structures": ["lattice", "sites", "charge"],
    "tables": ["index", "columns", "data"],
    "attachments": ["mime", "content"],
}

def format_cell(cell):
    cell = cell.strip()
    if not cell or cell.count(" ") > 1:
        return cell

    q = get_quantity(cell)
    if not q or isnan(q.nominal_value):
        return cell

    q = truncate_digits(q)
    return str(q.nominal_value) if isnan(q.std_dev) else str(q)

def new_error_units(measurement, quantity):
    if quantity.units == measurement.value.units:
        return measurement

    error = measurement.error.to(quantity.units)
    return ureg.Measurement(quantity, error)

def get_quantity(s):
    # 5, 5 eV, 5+/-1 eV, 5(1) eV
    # set uncertainty to nan if not provided
    parts = s.split()
    parts += [None] * (2 - len(parts))
    if isfloat(parts[0]):
        parts[0] += "+/-nan"

    try:
        parts[0] = ufloat_fromstr(parts[0])
        return ureg.Measurement(*parts)
    except ValueError:
        return None

def truncate_digits(q):
    if isnan(q.nominal_value):
        return q

    v = Decimal(str(q.nominal_value))
    vt = v.as_tuple()

    if vt.exponent >= 0:
        return q

    dgts = len(vt.digits)
    dgts = max_dgts if dgts > max_dgts else dgts
    s = f"{v:.{dgts}g}"
    if not isnan(q.std_dev):
        s += f"+/-{q.std_dev:.{dgts}g}"

    if q.units:
        s += f" {q.units}"

    return get_quantity(s)

def get_min_max(sender, path):
    # NOTE can't filter for project when using wildcard index data.$**
    # https://docs.mongodb.com/manual/core/index-wildcard/#wildcard-index-query-sort-support
    field = f"{path}{delimiter}value"
    key = f"{field}__type".replace(delimiter, "__")
    q = {key: "number"}  # NOTE need a query to trigger wildcard IXSCAN
    qs = sender.objects(**q).only(field).order_by(field)
    values = [get_value(doc, field) for doc in qs]
    values = [v for v in values if not isinstance(v, _Missing)]
    return (values[0], values[-1]) if len(values) else (None, None)

def get_resource(component):
    klass = component.capitalize()
    vmodule = import_module(f"mpcontribs.api.{component}.views")
    Resource = getattr(vmodule, f"{klass}Resource")
    return Resource()

def get_md5(resource, obj, fields):
    d = resource.serialize(obj, fields=fields)
    s = json.dumps(d, sort_keys=True).encode("utf-8")
    return md5(s).hexdigest()

class Contributions(DynamicDocument):
    project = LazyReferenceField(
        "Projects", required=True, passthrough=True, reverse_delete_rule=CASCADE
    )
    identifier = StringField(required=True, help_text="material/composition identifier")
    formula = StringField(help_text="formula (set dynamically if not provided)")
    is_public = BooleanField(
        required=True, default=False, help_text="public/private contribution"
    )
    last_modified = DateTimeField(
        required=True, default=datetime.utcnow, help_text="time of last modification"
    )
    data = DictField(
        default={}, validation=valid_dict, help_text="simple free-form data"
    )
    structures = ListField(
        ReferenceField("Structures", null=True), default=list, max_length=10
    )
    tables = ListField(
        ReferenceField("Tables", null=True), default=list, max_length=10
    )
    attachments = ListField(
        ReferenceField("Attachments", null=True), default=list, max_length=10
    )
    notebook = LazyReferenceField("Notebooks", passthrough=True)
    meta = {
        "collection": "contributions",
        "indexes": [
            "project",
            "identifier",
            "formula",
            "is_public",
            "last_modified",
            {"fields": [(r"data.$**", 1)]},
            "notebook",
        ]
        + list(COMPONENTS.keys()),
    }

    @classmethod
    def post_init(cls, sender, document, **kwargs):
        # replace existing components with according ObjectIds
        for component, fields in COMPONENTS.items():
            lst = document._data.get(component)
            if lst and lst[0].id is None:  # id is None for incoming POST
                resource = get_resource(component)
                for i, o in enumerate(lst):
                    digest = get_md5(resource, o, fields)
                    obj = resource.document.objects(md5=digest).only("id").first()
                    if obj:
                        lst[i] = obj.to_dbref()

    @classmethod
    def pre_save_post_validation(cls, sender, document, **kwargs):
        if kwargs.get("skip"):
            return

        # set formula field
        if hasattr(document, "formula") and not document.formula:
            formulae = current_app.config["FORMULAE"]
            document.formula = formulae.get(document.identifier, document.identifier)

        # project is LazyReferenceField
        project = document.project.fetch()

        # run data through Pint Quantities and save as dicts
        def make_quantities(path, key, value):
            if key in quantity_keys or not isinstance(value, (str, int, float)):
                return key, value

            str_value = str(value)
            if str_value.count(" ") > 1:
                return key, value

            q = get_quantity(str_value)
            if not q:
                return key, value

            # silently ignore "nan"
            if isnan(q.nominal_value):
                return False

            # try compact representation
            qq = q.value.to_compact()
            q = new_error_units(q, qq)

            # reduce dimensionality if possible
            if not q.check(0):
                qq = q.value.to_reduced_units()
                q = new_error_units(q, qq)

            # ensure that the same units are used across contributions
            field = delimiter.join(["data"] + list(path) + [key])
            try:
                column = project.columns.get(path=field)
                if column.unit != str(q.value.units):
                    qq = q.value.to(column.unit)
                    q = new_error_units(q, qq)
            except DoesNotExist:
                pass  # column doesn't exist yet (generated in post_save)
            except DimensionalityError:
                raise ValueError(
                    f"Can't convert [{q.units}] to [{column.unit}] for {field}!"
                )

            # significant digits
            q = truncate_digits(q)

            # return new value dict
            display = str(q.value) if isnan(q.std_dev) else str(q)
            value = {
                "display": display,
                "value": q.nominal_value,
                "error": q.std_dev,
                "unit": str(q.units),
            }
            return key, value

        document.data = remap(document.data, visit=make_quantities, enter=enter)
        document.last_modified = datetime.utcnow()

    @classmethod
    def post_save(cls, sender, document, **kwargs):
        if kwargs.get("skip"):
            return

        # project is LazyReferenceField
        project = document.project.fetch()

        # set columns field for project
        def update_columns(path, key, value):
            path = delimiter.join(["data"] + list(path) + [key])
            is_quantity = isinstance(value, dict) and quantity_keys.issubset(
                value.keys()
            )
            is_text = bool(
                not is_quantity and isinstance(value, str) and key not in quantity_keys
            )
            if is_quantity or is_text:
                project.reload("columns")
                try:
                    column = project.columns.get(path=path)
                    if is_quantity:
                        v = value["value"]
                        if isnan(column.max) or v > column.max:
                            column.max = v
                        if isnan(column.min) or v < column.min:
                            column.min = v

                except DoesNotExist:
                    column = {"path": path}
                    if is_quantity:
                        column["unit"] = value["unit"]
                        column["min"] = column["max"] = value["value"]

                    project.columns.create(**column)

                project.save().reload("columns")
                ncolumns = len(project.columns)
                if ncolumns > 50:
                    raise ValueError("Reached maximum number of columns (50)!")

            return True

        # run update_columns over document data
        remap(document.data, visit=update_columns, enter=enter)

        # add/remove columns for other components
        for path in COMPONENTS.keys():
            try:
                project.columns.get(path=path)
            except DoesNotExist:
                if getattr(document, path):
                    project.columns.create(path=path)
                    project.save().reload("columns")

    @classmethod
    def pre_delete(cls, sender, document, **kwargs):
        args = ["notebook"] + list(COMPONENTS.keys())
        document.reload(*args)
        deleted = defaultdict(list)

        for component in COMPONENTS.keys():
            # check if other contributions exist before deletion!
            for idx, obj in enumerate(getattr(document, component)):
                q = {component: obj.id}
                if sender.objects(**q).count() < 2:
                    obj.delete()
                    deleted[component].append(idx)

        # remove reference documents
        if document.notebook is not None:
            from mpcontribs.api.notebooks.document import Notebooks

            nid = document.notebook.id
            nb = Notebooks.objects(id=nid).first()
            nb.delete(signal_kwargs=deleted)

    @classmethod
    def post_delete(cls, sender, document, **kwargs):
        if kwargs.get("skip"):
            return

        # reset columns field for project
        project = document.project.fetch()

        for column in list(project.columns):
            if not isnan(column.min) and not isnan(column.max):
                column.min, column.max = get_min_max(sender, column.path)
                if isnan(column.min) and isnan(column.max):
                    # just deleted last contribution with this column
                    project.update(pull__columns__path=column.path)
            else:
                # use wildcard index if available -> single field query
                field = column.path.replace(delimiter, "__") + "__type"
                qs = sender.objects(**{field: "string"}).only(column.path)

                if qs.count() < 1 or qs.filter(project__name=project.name).count() < 1:
                    project.update(pull__columns__path=column.path)

signals.post_init.connect(Contributions.post_init, sender=Contributions)
signals.pre_save_post_validation.connect(
    Contributions.pre_save_post_validation, sender=Contributions
)
signals.post_save.connect(Contributions.post_save, sender=Contributions)
signals.pre_delete.connect(Contributions.pre_delete, sender=Contributions)
signals.post_delete.connect(Contributions.post_delete, sender=Contributions)
<|endoftext|>


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

"""
This node tries to generate odometrie by converting wheel encoder ticks from my
robot 'minibot'.
It then publishes odometrie messages into ROS for use in the navigation stack.

Author:  Markus Knapp, 2018
Website: https://direcs.de
"""

import logging
import sys
import time

import rospy
import math

from std_msgs.msg import Header, Float32
from sensor_msgs.msg import Imu

# see also http://wiki.ros.org/navigation/Tutorials/RobotSetup/Odom:
# "The nav_msgs/Odometry message stores an estimate of the position
#  and velocity of a robot in free space"

# see also ROS Odometry Python example
# https://gist.github.com/atotto/f2754f75bedb6ea56e3e0264ec405dcf
import nav_msgs.msg
from nav_msgs.msg import Odometry
# for the tf broadcaster
import tf
from geometry_msgs.msg import Point, Pose, Quaternion, Twist, Vector3

# sleep time for this node in seconds
sleepTime = 0.25

# initialise the ROS node
rospy.init_node('odom_node', anonymous=False)

# initial position
x  = 0.0
y  = 0.0
th = 0.0

# velocity
vx  = 0.4
vy  = 0.0
vth = 0.4
current_time = rospy.Time.now()
last_time = rospy.Time.now()
tfBroadcaster = tf.TransformBroadcaster()
degree = M_PI/180

""" robot parameters

:@# TODO: move these values to ROS parameter server
i.e.: node.getParam("odom/distancepercount", param_distancepercount)

16 Flankenwechsel pro Umdrehung,
durch Getriebe:
544 Flankenwechsel pro Umdrehung für ein 34:1 Getriebe

double DistancePerCount = 50.0/12.0 * 0.6/17.0 """
distancePerCount       = 0.0  # param_distancepercount   in meters (m)!!
lengthBetweenTwoWheels = 0.0  # param_widthbetweenwheels

# message declarations
# the tf broadcaster
# "any odometry source must publish information about the coordinate frame that it manages"
# org: geometry_msgs::TransformStamped odom_trans;
odom_trans = tf.TransformBroadcaster()
odom_trans.header.frame_id = "odom"
odom_trans.child_frame_id = "base_footprint"

# --------------------
# wheel encoder stuff
# --------------------
import atexit

# for signal handling
import signal
import sys

# count the ticks from the wheel encoders "forever"
odomCountForeverFrontLeft  = 0
odomCountForeverRearLeft   = 0
odomCountForeverFrontRight = 0
odomCountForeverRearRight  = 0

odomCountFrontLeft  = 0
odomCountRearLeft   = 0
odomCountFrontRight = 0
odomCountRearRight  = 0

# this is one tick from a wheel encoder
odomStep = 1

# for getting the hostname of the underlying system
import socket
# showing hostname
hostname = socket.gethostname()
rospy.loginfo("Running on host %s.", hostname)
if (hostname != 'minibot') and (hostname != 'minibottest'):
    rospy.logwarn("Test mode only due to other host. Skipping all hardware staff!")
else:
    # we are "live" on robot hardware
    import RPi.GPIO as GPIO

    # GPIO init
    GPIO.setmode(GPIO.BCM) # use the BCM-GPIO names, _not_ the pin numbers on the board

    # Raspberry Pi GPIO configuration:
    frontLeftEncoderGPIO  = 27 # forward
    rearLeftEncoderGPIO   = 22 # forward
    frontRightEncoderGPIO = 23 # forward
    rearRightEncoderGPIO  = 24 # forward

    # setup
    rospy.loginfo("Wheel encoder GPIO setup...")
    GPIO.setup(frontLeftEncoderGPIO,  GPIO.IN)
    GPIO.setup(rearLeftEncoderGPIO,   GPIO.IN)
    GPIO.setup(frontRightEncoderGPIO, GPIO.IN)
    GPIO.setup(rearRightEncoderGPIO,  GPIO.IN)

    # encoder pulse detection by interrupt
    def fronLeftEncoderCallback(answer):
        global odomCountFrontLeft
        global odomCountForeverFrontLeft
        # velocity is always positive, so we always increment
        odomCountFrontLeft        = odomCountFrontLeft        + odomStep
        odomCountForeverFrontLeft = odomCountForeverFrontLeft + odomStep
        rospy.loginfo("Front left encoder.")

    def rearLeftEncoderCallback(answer):
        global odomCountRearLeft
        global odomCountForeverRearLeft
        odomCountRearLeft        = odomCountRearLeft        + odomStep
        odomCountForeverRearLeft = odomCountForeverRearLeft + odomStep
        rospy.loginfo("Rear left encoder.")

    def frontRightEncoderCallback(answer):
        global odomCountFrontRight
        global odomCountForeverFrontRight
        odomCountFrontRight        = odomCountFrontRight        + odomStep
        odomCountForeverFrontRight = odomCountForeverFrontRight + odomStep
        rospy.loginfo("Front right encoder.")

    def rearRightEncoderCallback(answer):
        global odomCountRearRight
        global odomCountForeverRearRight
        odomCountRearRight        = odomCountRearRight        + odomStep
        odomCountForeverRearRight = odomCountForeverRearRight + odomStep
        rospy.loginfo("Rear right encoder.")

    # add GPIO event detectors (interrupt service routines)
    rospy.loginfo("registering event handlers...")

    # enabling event handlers (if needed only)
    def enableEncoderTracking():
        GPIO.add_event_detect(frontLeftEncoderGPIO,  GPIO.FALLING, callback=fronLeftEncoderCallback)
        GPIO.add_event_detect(rearLeftEncoderGPIO, GPIO.FALLING, callback=rearLeftEncoderCallback)
        GPIO.add_event_detect(frontRightEncoderGPIO, GPIO.FALLING, callback=frontRightEncoderCallback)
        GPIO.add_event_detect(rearRightEncoderGPIO, GPIO.FALLING, callback=rearRightEncoderCallback)

    # disabling event handlers
    def disableEncoderTracking():
        GPIO.remove_event_detect(frontLeftEncoderGPIO)
        GPIO.remove_event_detect(rearLeftEncoderGPIO)
        GPIO.remove_event_detect(frontRightEncoderGPIO)
        GPIO.remove_event_detect(rearRightEncoderGPIO)

    """ START wheel encoder tracking """
    enableEncoderTracking()

# define a clean node exit
def my_exit():
  rospy.loginfo("Shutting down odometry service...")
  # run some parts only on the real robot
  if hostname == 'minibot' or hostname == 'minibottest':
        """ STOP wheel encoder tracking """
        disableEncoderTracking()
        GPIO.cleanup()
  rospy.loginfo("...shutting down odometrie service complete.")

# call this method on node exit
rospy.on_shutdown(my_exit)

""" this is where the odometry magic happens """
def calculateOdometry():
    # get current time
    current_time = rospy.Time.now()

	XXX = (0.1 / 50.0)

    # calculate passed time
    dt = (current_time - odo_last_time).toSec()

#	if (dt>=ODOM_PERIOD)
#	{
	# https://answers.ros.org/question/207392/generating-odom-message-from-encoder-ticks-for-robot_pose_ekf/
	# http://www.seattlerobotics.org/encoder/200610/Article3/IMU%20Odometry,%20by%20David%20Anderson.htm

    # extract the wheel velocities from the tick signals count
    deltaFrontLeft  = odomCountFrontLeft
    deltaRearLeft   = odomCountRearLeft
    deltaFrontRight = odomCountFrontRight
    deltaRearRight  = odomCountRearRight

    v_FrontLeft  = (deltaFrontLeft  * distancePerCount) / dt
    v_RearLeft   = (deltaRearLeft   * distancePerCount) / dt
    v_FrontRight = (deltaFrontRight * distancePerCount) / dt
    v_RearRight  = (deltaRearRight  * distancePerCount) / dt

    #motor_md[MOTOR_L].odom_rate = v_left;
    #motor_md[MOTOR_R].odom_rate = v_right;

    # :@TODO what about the rear values?!?
    motor_md[MOTOR_L].odom_rate = (4.0 * motor_md[MOTOR_L].odom_rate + v_FrontLeft) / 5.0
    motor_md[MOTOR_R].odom_rate = (4.0 * motor_md[MOTOR_R].odom_rate + v_FrontRight) / 5.0

    #motor_md[MOTOR_L].odom_rate = (19.0 * motor_md[MOTOR_L].odom_rate + v_left) / 20.0;
    #motor_md[MOTOR_R].odom_rate = (19.0 * motor_md[MOTOR_R].odom_rate + v_right) / 20.0;

    # :@TODO what about the rear values?!?
    vx = ((v_FrontRight + v_FrontLeft) / 2)
    vy = 0
    # :@TODO declare and define lengthBetweenTwoWheels
    # :@TODO what about the rear values?!?
    vth = ((v_FrontRight - v_FrontLeft) / lengthBetweenTwoWheels)

    #double dt = (current_time - last_time).toSec();
    delta_x = (vx * cos(th)) * dt
    delta_y = (vx * sin(th)) * dt
    delta_th = vth * dt

    x += delta_x
    y += delta_y
    th += delta_th

    # org: geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th);
    # since all odometry is 6DOF we'll need a quaternion created from yaw
    odom_quat = tf::createQuaternionMsgFromYaw(th)
    # oder von imu_bno055:     odom_quat = tf.transformations.quaternion_from_euler(x, y, z) #   z vs. th ?!??

    """ generate tf broacast message and publish """
    odom_trans = Odometry() # Line taken from imu_bno055.py (and disabled there!)
	odom_trans.header.stamp = current_time
	odom_trans.header.frame_id = "odom"
	odom_trans.child_frame_id = "base_footprint"

	odom_trans.transform.translation.x = x
	odom_trans.transform.translation.y = y
	odom_trans.transform.translation.z = 0.0
	odom_trans.transform.rotation = odom_quat

	# send the transform
	tfBroadcaster.sendTransform(odom_trans)

	""" Odometry message """
	nav_msgs::Odometry odom;
	odom.header.stamp = current_time;
	//odom.header.frame_id = "odom";

	//set the position
	odom.pose.pose.position.x = x;
	odom.pose.pose.position.y = y;
	odom.pose.pose.position.z = 0.0;
	odom.pose.pose.orientation = odom_quat;
	double max = 1000000000000.0;
	double min = 0.001;
	odom.pose.covariance = {
			min,    0.0, 0.0,  0.0,  0.0,  0.0,
			0.0,    min, 0.0,  0.0,  0.0,  0.0,
			0.0,    0.0, max,  0.0,  0.0,  0.0,
			0.0,    0.0, 0.0,  max,  0.0,  0.0,
			0.0,    0.0, 0.0,  0.0,  max,  0.0,
			0.0,    0.0, 0.0,  0.0,  0.0,  max };

	//set the velocity
	//odom.child_frame_id = "base_footprint";
	odom.twist.twist.linear.x = vx;
	odom.twist.twist.linear.y = vy;
	odom.twist.twist.angular.z = vth;
	odom.twist.covariance = {
			min,     0.0,  0.0,  0.0,  0.0,  0.0,
			0.0,     min,  0.0,  0.0,  0.0,  0.0,
			0.0,     0.0,  max,  0.0,  0.0,  0.0,
			0.0,     0.0,  0.0,  max,  0.0,  0.0,
			0.0,     0.0,  0.0,  0.0,  max,  0.0,
			0.0,     0.0,  0.0,  0.0,  0.0,  max };

	ROS_DEBUG("handleODO() dt=%f encoder=%d,%d,%d,%d position=%f,%f twist=%f,%f,%f ",
			dt,
			motor_md[MOTOR_R].odom_cnt,motor_md[MOTOR_R].odom_step,
			motor_md[MOTOR_L].odom_cnt,motor_md[MOTOR_L].odom_step,
			x,
			y,
			vx,
			vy,
			vth);

	//publish the message
	odom_pub.publish(odom);

	odo_last_time = current_time;

	#
	# insert PID mode used/not used stuff here
	#

	#
	# publish diagnostic data
	#
	std_msgs::Float32 msg;

	msg.data = motor_md[MOTOR_L].setpoint;
	motor_pub_setpoint[MOTOR_L].publish(msg);
	msg.data = motor_md[MOTOR_R].setpoint;
	motor_pub_setpoint[MOTOR_R].publish(msg);

	msg.data = motor_md[MOTOR_L].value;
	motor_pub_value[MOTOR_L].publish(msg);
	msg.data = motor_md[MOTOR_R].value;
	motor_pub_value[MOTOR_R].publish(msg);

	msg.data = motor_md[MOTOR_L].odom_rate/XXX;
	motor_pub_rate[MOTOR_L].publish(msg);
	msg.data = motor_md[MOTOR_R].odom_rate/XXX;
	motor_pub_rate[MOTOR_R].publish(msg);

	msg.data = motor_md[MOTOR_L].odom_cnt_forever;
	motor_pub_odomcnt[MOTOR_L].publish(msg);
	msg.data = motor_md[MOTOR_R].odom_cnt_forever;
	motor_pub_odomcnt[MOTOR_R].publish(msg);

    # reset odom tick counters
	motor_md[MOTOR_L].odom_cnt = 0;
	motor_md[MOTOR_R].odom_cnt = 0;

""" 'main' """
while not rospy.is_shutdown():
    # measure staff and publish odom
    calculateOdometry()

    # Sleep for a second until the next reading.
    rospy.sleep(sleepTime)
<commit_msg>Fixed tf and odom type, moved declarations of tf broadcaster<commit_after>#!/usr/bin/env python
# coding=utf-8

"""
This node tries to generate odometrie by converting wheel encoder ticks from my
robot 'minibot'.
It then publishes odometrie messages into ROS for use in the navigation stack.

Author:  Markus Knapp, 2018
Website: https://direcs.de
"""

import logging
import sys
import time

import rospy
import math

from std_msgs.msg import Header, Float32
from sensor_msgs.msg import Imu

# see also http://wiki.ros.org/navigation/Tutorials/RobotSetup/Odom:
# "The nav_msgs/Odometry message stores an estimate of the position
#  and velocity of a robot in free space"

# see also ROS Odometry Python example
# https://gist.github.com/atotto/f2754f75bedb6ea56e3e0264ec405dcf
import nav_msgs.msg
from nav_msgs.msg import Odometry
# for the tf broadcaster
import tf
from geometry_msgs.msg import Point, Pose, Quaternion, Twist, Vector3

# sleep time for this node in seconds
sleepTime = 0.25

# initialise the ROS node
rospy.init_node('odom_node', anonymous=False)

# initial position
x  = 0.0
y  = 0.0
th = 0.0

# velocity
vx  = 0.4
vy  = 0.0
vth = 0.4
current_time = rospy.Time.now()
last_time = rospy.Time.now()
tfBroadcaster = tf.TransformBroadcaster()
degree = M_PI/180

""" robot parameters

:@# TODO: move these values to ROS parameter server
i.e.: node.getParam("odom/distancepercount", param_distancepercount)

16 Flankenwechsel pro Umdrehung,
durch Getriebe:
544 Flankenwechsel pro Umdrehung für ein 34:1 Getriebe

double DistancePerCount = 50.0/12.0 * 0.6/17.0 """
distancePerCount       = 0.0  # param_distancepercount   in meters (m)!!
lengthBetweenTwoWheels = 0.0  # param_widthbetweenwheels

# --------------------
# wheel encoder stuff
# --------------------
import atexit

# for signal handling
import signal
import sys

# count the ticks from the wheel encoders "forever"
odomCountForeverFrontLeft  = 0
odomCountForeverRearLeft   = 0
odomCountForeverFrontRight = 0
odomCountForeverRearRight  = 0

odomCountFrontLeft  = 0
odomCountRearLeft   = 0
odomCountFrontRight = 0
odomCountRearRight  = 0

# this is one tick from a wheel encoder
odomStep = 1

# for getting the hostname of the underlying system
import socket
# showing hostname
hostname = socket.gethostname()
rospy.loginfo("Running on host %s.", hostname)
if (hostname != 'minibot') and (hostname != 'minibottest'):
    rospy.logwarn("Test mode only due to other host. Skipping all hardware staff!")
else:
    # we are "live" on robot hardware
    import RPi.GPIO as GPIO

    # GPIO init
    GPIO.setmode(GPIO.BCM) # use the BCM-GPIO names, _not_ the pin numbers on the board

    # Raspberry Pi GPIO configuration:
    frontLeftEncoderGPIO  = 27 # forward
    rearLeftEncoderGPIO   = 22 # forward
    frontRightEncoderGPIO = 23 # forward
    rearRightEncoderGPIO  = 24 # forward

    # setup
    rospy.loginfo("Wheel encoder GPIO setup...")
    GPIO.setup(frontLeftEncoderGPIO,  GPIO.IN)
    GPIO.setup(rearLeftEncoderGPIO,   GPIO.IN)
    GPIO.setup(frontRightEncoderGPIO, GPIO.IN)
    GPIO.setup(rearRightEncoderGPIO,  GPIO.IN)

    # encoder pulse detection by interrupt
    def fronLeftEncoderCallback(answer):
        global odomCountFrontLeft
        global odomCountForeverFrontLeft
        # velocity is always positive, so we always increment
        odomCountFrontLeft        = odomCountFrontLeft        + odomStep
        odomCountForeverFrontLeft = odomCountForeverFrontLeft + odomStep
        rospy.loginfo("Front left encoder.")

    def rearLeftEncoderCallback(answer):
        global odomCountRearLeft
        global odomCountForeverRearLeft
        odomCountRearLeft        = odomCountRearLeft        + odomStep
        odomCountForeverRearLeft = odomCountForeverRearLeft + odomStep
        rospy.loginfo("Rear left encoder.")

    def frontRightEncoderCallback(answer):
        global odomCountFrontRight
        global odomCountForeverFrontRight
        odomCountFrontRight        = odomCountFrontRight        + odomStep
        odomCountForeverFrontRight = odomCountForeverFrontRight + odomStep
        rospy.loginfo("Front right encoder.")

    def rearRightEncoderCallback(answer):
        global odomCountRearRight
        global odomCountForeverRearRight
        odomCountRearRight        = odomCountRearRight        + odomStep
        odomCountForeverRearRight = odomCountForeverRearRight + odomStep
        rospy.loginfo("Rear right encoder.")

    # add GPIO event detectors (interrupt service routines)
    rospy.loginfo("registering event handlers...")

    # enabling event handlers (if needed only)
    def enableEncoderTracking():
        GPIO.add_event_detect(frontLeftEncoderGPIO,  GPIO.FALLING, callback=fronLeftEncoderCallback)
        GPIO.add_event_detect(rearLeftEncoderGPIO, GPIO.FALLING, callback=rearLeftEncoderCallback)
        GPIO.add_event_detect(frontRightEncoderGPIO, GPIO.FALLING, callback=frontRightEncoderCallback)
        GPIO.add_event_detect(rearRightEncoderGPIO, GPIO.FALLING, callback=rearRightEncoderCallback)

    # disabling event handlers
    def disableEncoderTracking():
        GPIO.remove_event_detect(frontLeftEncoderGPIO)
        GPIO.remove_event_detect(rearLeftEncoderGPIO)
        GPIO.remove_event_detect(frontRightEncoderGPIO)
        GPIO.remove_event_detect(rearRightEncoderGPIO)

    """ START wheel encoder tracking """
    enableEncoderTracking()

# define a clean node exit
def my_exit():
  rospy.loginfo("Shutting down odometry service...")
  # run some parts only on the real robot
  if hostname == 'minibot' or hostname == 'minibottest':
        """ STOP wheel encoder tracking """
        disableEncoderTracking()
        GPIO.cleanup()
  rospy.loginfo("...shutting down odometrie service complete.")

# call this method on node exit
rospy.on_shutdown(my_exit)

""" this is where the odometry magic happens """
def calculateOdometry():
    # get current time
    current_time = rospy.Time.now()

	XXX = (0.1 / 50.0)

    # calculate passed time
    dt = (current_time - odo_last_time).toSec()

#	if (dt>=ODOM_PERIOD)
#	{
	# https://answers.ros.org/question/207392/generating-odom-message-from-encoder-ticks-for-robot_pose_ekf/
	# http://www.seattlerobotics.org/encoder/200610/Article3/IMU%20Odometry,%20by%20David%20Anderson.htm

    # extract the wheel velocities from the tick signals count
    deltaFrontLeft  = odomCountFrontLeft
    deltaRearLeft   = odomCountRearLeft
    deltaFrontRight = odomCountFrontRight
    deltaRearRight  = odomCountRearRight

    v_FrontLeft  = (deltaFrontLeft  * distancePerCount) / dt
    v_RearLeft   = (deltaRearLeft   * distancePerCount) / dt
    v_FrontRight = (deltaFrontRight * distancePerCount) / dt
    v_RearRight  = (deltaRearRight  * distancePerCount) / dt

    #motor_md[MOTOR_L].odom_rate = v_left;
    #motor_md[MOTOR_R].odom_rate = v_right;

    # :@TODO what about the rear values?!?
    motor_md[MOTOR_L].odom_rate = (4.0 * motor_md[MOTOR_L].odom_rate + v_FrontLeft) / 5.0
    motor_md[MOTOR_R].odom_rate = (4.0 * motor_md[MOTOR_R].odom_rate + v_FrontRight) / 5.0

    #motor_md[MOTOR_L].odom_rate = (19.0 * motor_md[MOTOR_L].odom_rate + v_left) / 20.0;
    #motor_md[MOTOR_R].odom_rate = (19.0 * motor_md[MOTOR_R].odom_rate + v_right) / 20.0;

    # :@TODO what about the rear values?!?
    vx = ((v_FrontRight + v_FrontLeft) / 2)
    vy = 0
    # :@TODO declare and define lengthBetweenTwoWheels
    # :@TODO what about the rear values?!?
    vth = ((v_FrontRight - v_FrontLeft) / lengthBetweenTwoWheels)

    #double dt = (current_time - last_time).toSec();
    delta_x = (vx * cos(th)) * dt
    delta_y = (vx * sin(th)) * dt
    delta_th = vth * dt

    x += delta_x
    y += delta_y
    th += delta_th

    # org: geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th);
    # since all odometry is 6DOF we'll need a quaternion created from yaw
    odom_quat = tf::createQuaternionMsgFromYaw(th)
    # oder von imu_bno055:     odom_quat = tf.transformations.quaternion_from_euler(x, y, z) #   z vs. th ?!??

    """ generate tf broacast message and publish """
    # message declarations
    # the tf broadcaster
    # "any odometry source must publish information about the coordinate frame that it manages"
    # org: geometry_msgs::TransformStamped odom_trans;
    odom_trans = tf.TransformBroadcaster()
	odom_trans.header.stamp = current_time
	odom_trans.header.frame_id = "odom"
	odom_trans.child_frame_id = "base_footprint"

	odom_trans.transform.translation.x = x
	odom_trans.transform.translation.y = y
	odom_trans.transform.translation.z = 0.0
	odom_trans.transform.rotation = odom_quat

	# send the transform
	tfBroadcaster.sendTransform(odom_trans)

	""" Odometry message """
	odom = Odometry()
	odom.header.stamp = current_time;
	//odom.header.frame_id = "odom";

	//set the position
	odom.pose.pose.position.x = x;
	odom.pose.pose.position.y = y;
	odom.pose.pose.position.z = 0.0;
	odom.pose.pose.orientation = odom_quat;
	double max = 1000000000000.0;
	double min = 0.001;
	odom.pose.covariance = {
			min,    0.0, 0.0,  0.0,  0.0,  0.0,
			0.0,    min, 0.0,  0.0,  0.0,  0.0,
			0.0,    0.0, max,  0.0,  0.0,  0.0,
			0.0,    0.0, 0.0,  max,  0.0,  0.0,
			0.0,    0.0, 0.0,  0.0,  max,  0.0,
			0.0,    0.0, 0.0,  0.0,  0.0,  max };

	//set the velocity
	//odom.child_frame_id = "base_footprint";
	odom.twist.twist.linear.x = vx;
	odom.twist.twist.linear.y = vy;
	odom.twist.twist.angular.z = vth;
	odom.twist.covariance = {
			min,     0.0,  0.0,  0.0,  0.0,  0.0,
			0.0,     min,  0.0,  0.0,  0.0,  0.0,
			0.0,     0.0,  max,  0.0,  0.0,  0.0,
			0.0,     0.0,  0.0,  max,  0.0,  0.0,
			0.0,     0.0,  0.0,  0.0,  max,  0.0,
			0.0,     0.0,  0.0,  0.0,  0.0,  max };

	ROS_DEBUG("handleODO() dt=%f encoder=%d,%d,%d,%d position=%f,%f twist=%f,%f,%f ",
			dt,
			motor_md[MOTOR_R].odom_cnt,motor_md[MOTOR_R].odom_step,
			motor_md[MOTOR_L].odom_cnt,motor_md[MOTOR_L].odom_step,
			x,
			y,
			vx,
			vy,
			vth);

	//publish the message
	odom_pub.publish(odom);

	odo_last_time = current_time;

	#
	# insert PID mode used/not used stuff here
	#

	#
	# publish diagnostic data
	#
	std_msgs::Float32 msg;

	msg.data = motor_md[MOTOR_L].setpoint;
	motor_pub_setpoint[MOTOR_L].publish(msg);
	msg.data = motor_md[MOTOR_R].setpoint;
	motor_pub_setpoint[MOTOR_R].publish(msg);

	msg.data = motor_md[MOTOR_L].value;
	motor_pub_value[MOTOR_L].publish(msg);
	msg.data = motor_md[MOTOR_R].value;
	motor_pub_value[MOTOR_R].publish(msg);

	msg.data = motor_md[MOTOR_L].odom_rate/XXX;
	motor_pub_rate[MOTOR_L].publish(msg);
	msg.data = motor_md[MOTOR_R].odom_rate/XXX;
	motor_pub_rate[MOTOR_R].publish(msg);

	msg.data = motor_md[MOTOR_L].odom_cnt_forever;
	motor_pub_odomcnt[MOTOR_L].publish(msg);
	msg.data = motor_md[MOTOR_R].odom_cnt_forever;
	motor_pub_odomcnt[MOTOR_R].publish(msg);

    # reset odom tick counters
	motor_md[MOTOR_L].odom_cnt = 0;
	motor_md[MOTOR_R].odom_cnt = 0;

""" 'main' """
while not rospy.is_shutdown():
    # measure staff and publish odom
    calculateOdometry()

    # Sleep for a second until the next reading.
    rospy.sleep(sleepTime)
<|endoftext|>


<commit_before>#!/usr/bin/python

"""
Simple script to force the audio device and levels for audio in and out

FIXME: Determine optimum input and output volumes for other device types
"""

import os
import sys
import subprocess

# Device preferences
# (higher preference level means USE ME FIRST)
device_prefs = {
    'Chat' : {
        'preference' : 100,
        'volume_out': 50,
        'volume_in' : 70,
        },
    'Jabra SPEAK' : {
        'preference' : 90,
        'volume_out': 83,
        'volume_in' : 100,
    },
    'Phnx Spider' : {
        'preference' : 110,
        'volume_out': 60,
        'volume_in' : 60,
    },
    'MDR-10RBT' : {
        'preference' : 10,
        'volume_out': 60,
        'volume_in' : 60,
    }
}

debug=True

# cli utility to change audio device, found on github
# https://github.com/deweller/switchaudio-osx
# needs to be compiled with xcode and 'installed' somehwhere locally
audioswitcher='%s/forceaudio/audioswitcher' % os.getenv("HOME")

def run_me(cmd):
    p = subprocess.Popen(cmd, shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
    # Check exit code
    if p.wait() != 0:
        print('Error running: %s' % cmd)
        print(p.stdout.read())
        sys.exit(2)
    return(p)

def set_input(device):
    run_me('%s -t input -s "%s"' % (audioswitcher, device))

def set_output(device):
    run_me('%s -t output -s "%s"' % (audioswitcher, device))
    return(True)

def list_devices():
    p = run_me('%s -a' % (audioswitcher))
    return(p.stdout.readlines())

# NOTE: osascript with these options work in 10.10, but may fail <10.9
def set_output_volume(level=80):
    run_me('osascript -e "set Volume output volume %s"' % (level))
    return(True)

def set_input_volume(level=80):
    run_me('osascript -e "set Volume input volume %s"' % (level))
    return(True)

# Main
if __name__ == "__main__":
    device=False
    preference=0

    if debug: print 'Devices Found:'
    for line in list_devices():
        if debug: print line,

        # Check device list against known devices
        for knowndevice in device_prefs.keys():
            if knowndevice in line:

                # If device has a higher preference, use it.
                if device_prefs[knowndevice]['preference'] > preference:
                    device = line.split("(")[0].strip()
                    preference = device_prefs[knowndevice]['preference']
                    volume_out = device_prefs[knowndevice]['volume_out']
                    volume_in  = device_prefs[knowndevice]['volume_in']

    if device:
        print 'Forcing Audio Device:\n', device, volume_out, volume_in
        set_input(device)
        set_output(device)

        set_output_volume(volume_out)
        set_input_volume(volume_in)
<commit_msg>Update forceaudio.py<commit_after>#!/usr/bin/python

"""
Simple script to force the audio device and levels for audio in and out

FIXME: Determine optimum input and output volumes for other device types
"""

import os
import sys
import subprocess

# Device preferences
# (higher preference level means USE ME FIRST)
# volume_out and volume_in are a %
device_prefs = {
    'Chat' : {
        'preference' : 100,
        'volume_out': 70,
        'volume_in' : 70,
        },
    'Jabra SPEAK' : {
        'preference' : 90,
        'volume_out': 70,
        'volume_in' : 100,
    },
    'Phnx Spider' : {
        'preference' : 110,
        'volume_out': 35,
        'volume_in' : 90,
    },
    'MDR-10RBT' : {
        'preference' : 10,
        'volume_out': 60,
        'volume_in' : 60,
    }
}

debug=True

# cli utility to change audio device, found on github
# https://github.com/deweller/switchaudio-osx
# needs to be compiled with xcode and 'installed' somehwhere locally
audioswitcher='%s/forceaudio/audioswitcher' % os.getenv("HOME")

def run_me(cmd):
    p = subprocess.Popen(cmd, shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
    # Check exit code
    if p.wait() != 0:
        print('Error running: %s' % cmd)
        print(p.stdout.read())
        sys.exit(2)
    return(p)

def set_input(device):
    run_me('%s -t input -s "%s"' % (audioswitcher, device))

def set_output(device):
    run_me('%s -t output -s "%s"' % (audioswitcher, device))
    return(True)

def list_devices():
    p = run_me('%s -a' % (audioswitcher))
    return(p.stdout.readlines())

# NOTE: osascript with these options work in 10.10, but may fail <10.9
def set_output_volume(level=80):
    run_me('osascript -e "set Volume output volume %s"' % (level))
    return(True)

def set_input_volume(level=80):
    run_me('osascript -e "set Volume input volume %s"' % (level))
    return(True)

# Main
if __name__ == "__main__":
    device=False
    preference=0

    if debug: print 'Devices Found:'
    for line in list_devices():
        if debug: print line,

        # Check device list against known devices
        for knowndevice in device_prefs.keys():
            if knowndevice in line:

                # If device has a higher preference, use it.
                if device_prefs[knowndevice]['preference'] > preference:
                    device = line.split("(")[0].strip()
                    preference = device_prefs[knowndevice]['preference']
                    volume_out = device_prefs[knowndevice]['volume_out']
                    volume_in  = device_prefs[knowndevice]['volume_in']

    if device:
        print 'Forcing Audio Device:\n', device, volume_out, volume_in
        set_input(device)
        set_output(device)

        set_output_volume(volume_out)
        set_input_volume(volume_in)
<|endoftext|>


<commit_before>import plugins
import os
from subprocess import Popen, PIPE

def meminfo_win():
    info = Popen('tasklist /fo list /fi "pid eq %d"' % os.getpid(), shell=True, stdout=PIPE).communicate()[0]
    info = info.strip()
    minfo = info.splitlines()[-1]
    return minfo.split(":", 1)[-1].strip()

def meminfo(connection, channel, nick, cmd, args):
    if plugins.is_windows():
        minfo = meminfo_win()
    else:
        minfo = None
    plugins.say(connection, channel, "Current memory usage: %s" % minfo)

plugins.register_pub_cmd("meminfo", meminfo)
<commit_msg>add meminfo for linux<commit_after>import plugins
import os
from subprocess import Popen, PIPE

def meminfo_win():
    info = Popen('tasklist /fo list /fi "pid eq %d"' % os.getpid(), shell=True, stdout=PIPE).communicate()[0]
    info = info.strip()
    minfo = info.splitlines()[-1]
    return minfo.split(":", 1)[-1].strip()

def meminfo(connection, channel, nick, cmd, args):
    if plugins.is_windows():
        minfo = meminfo_win()
    else:
    	import resource
        minfo = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + " kB"
    plugins.say(connection, channel, "Current memory usage: %s" % minfo)

plugins.register_pub_cmd("meminfo", meminfo)
<|endoftext|>


<commit_before>import datetime
import couchdb
from app.logger import LOGGER as log
from app.tweet_analytics import TweetAnalytics
from app.db import DB, AnalyticsLog
import settings

database = DB(settings.PG_DB_USER,
              settings.PG_DB_PASSWORD, settings.PG_DB_NAME)
database.connect()

server = couchdb.Server(url='http://127.0.0.1:15984/')
db = server['tweets']

# get twitter-hashtags couchdb instance
try:
    hashtags = server.create["twitter-hashtags"]
except:
    hashtags = server["twitter-hashtags"]

# get twitter-words couchdb instance
try:
    words = server.create["twitter-words"]
except:
    words = server["twitter-words"]

# get twitter-users couchdb instance
try:
    user = server.create["twitter-users"]
except:
    user = server["twitter-users"]

log.info("START - Processing analytics data")

analytic_db = AnalyticsLog(database.con, database.meta)
date_list = analytic_db.fetch_unprocessed_data()

# fetch data for individual date
for date_for_analysis in date_list:
    log.info("START - Process data for %s", date_for_analysis)
    view_data = []

    for row in db.view('_design/analytics/_view/tweets-victoria',\
                         startkey=date_for_analysis, endkey=date_for_analysis):
        view_data.append(row.value)

    log.info("Processing %d row of data", len(view_data))

    analytics = TweetAnalytics(
        date_for_analysis, view_data, db, hashtags, words, user)
    analytics.process_data()
    analytic_db.update_timestamp_data(date_for_analysis)
    log.info("END - Process data for %s", date_for_analysis)

log.info("END - Processing analytics data")<commit_msg>change couchdb server value to be fetched from settings<commit_after>import datetime
import couchdb
from app.logger import LOGGER as log
from app.tweet_analytics import TweetAnalytics
from app.db import DB, AnalyticsLog
import settings

database = DB(settings.PG_DB_USER,
              settings.PG_DB_PASSWORD, settings.PG_DB_NAME)
database.connect()

server = couchdb.Server(url=settings.COUCHDB_SERVER)
db = server['tweets']

# get twitter-hashtags couchdb instance
try:
    hashtags = server.create["twitter-hashtags"]
except:
    hashtags = server["twitter-hashtags"]

# get twitter-words couchdb instance
try:
    words = server.create["twitter-words"]
except:
    words = server["twitter-words"]

# get twitter-users couchdb instance
try:
    user = server.create["twitter-users"]
except:
    user = server["twitter-users"]

log.info("START - Processing analytics data")

analytic_db = AnalyticsLog(database.con, database.meta)
date_list = analytic_db.fetch_unprocessed_data()

# fetch data for individual date
for date_for_analysis in date_list:
    log.info("START - Process data for %s", date_for_analysis)
    view_data = []

    for row in db.view('_design/analytics/_view/tweets-victoria',\
                         startkey=date_for_analysis, endkey=date_for_analysis):
        view_data.append(row.value)

    log.info("Processing %d row of data", len(view_data))

    analytics = TweetAnalytics(
        date_for_analysis, view_data, db, hashtags, words, user)
    analytics.process_data()
    analytic_db.update_timestamp_data(date_for_analysis)
    log.info("END - Process data for %s", date_for_analysis)

log.info("END - Processing analytics data")<|endoftext|>