<commit_before>import curses
import os
import sys
import re
import shutil
import configparser
import json

import requests
import requests_cache

# GLOBALS!
basedir = os.path.join(os.path.expanduser('~'), '.cryptop')
datafile = os.path.join(basedir, 'wallet.json')
conffile = os.path.join(basedir, 'config.ini')
config = configparser.ConfigParser()
p = re.compile('[A-Z]{2,5},\d{0,}\.?\d{0,}')

def read_configuration(confpath):
    # copy our default config file
    if not os.path.isfile(confpath):
        defaultconf = os.path.join(os.path.dirname(__file__), 'config.ini')
        shutil.copyfile(defaultconf, conffile)

    config.read(confpath)
    return config

def if_coin(coin, url='https://www.cryptocompare.com/api/data/coinlist/'):
    '''Check if coin exists'''
    return coin in requests.get(url).json()['Data']

def get_price(coin, curr=None):
    '''Get the data on coins'''
    curr = curr or config['api'].get('currency', 'USD')
    fmt = 'https://min-api.cryptocompare.com/data/pricemultifull?fsyms={}&tsyms={}'

    try:
        r = requests.get(fmt.format(coin, curr))
    except requests.exceptions.RequestException:
        sys.exit('Could not complete request')

    try:
        data_raw = r.json()['RAW']
        return [(data_raw[c][curr]['PRICE'],
                data_raw[c][curr]['HIGH24HOUR'],
                data_raw[c][curr]['LOW24HOUR']) for c in coin.split(',')]
    except:
        sys.exit('Could not parse data')

def get_theme_colors():
    ''' Returns curses colors according to the config'''
    def get_curses_color(name_or_value):
        try:
            return getattr(curses, 'COLOR_' + name_or_value.upper())
        except AttributeError:
            return int(name_or_value)

    theme_config = config['theme']
    return (get_curses_color(theme_config.get('text', 'yellow')),
        get_curses_color(theme_config.get('banner', 'yellow')),
        get_curses_color(theme_config.get('banner_text', 'black')),
        get_curses_color(theme_config.get('background', -1)))

def conf_scr():
    '''Configure the screen and colors/etc'''
    curses.curs_set(0)
    curses.start_color()
    curses.use_default_colors()
    text, banner, banner_text, background = get_theme_colors()
    curses.init_pair(2, text, background)
    curses.init_pair(3, banner_text, banner)

def write_scr(stdscr, wallet, y, x):
    '''Write text and formatting to screen'''
    if y >= 1:
        stdscr.addnstr(0, 0, 'cryptop v0.1.2', x, curses.color_pair(2))
    if y >= 2:
        header = '  COIN      PRICE          HELD        VAL     HIGH      LOW  '
        stdscr.addnstr(1, 0, header, x, curses.color_pair(3))

    total = 0
    coinl = list(wallet.keys())
    heldl = list(wallet.values())
    if coinl:
        coinvl = get_price(','.join(coinl))

        if y > 3:
            for coin, val, held in zip(coinl, coinvl, heldl):
                if coinl.index(coin) + 2 < y:
                    stdscr.addnstr(coinl.index(coin) + 2, 0,
                        '  {:<5}  {:8.2f} {:>13.8f} {:10.2f} {:8.2f} {:8.2f}'
                        .format(coin, val[0], float(held), float(held) * val[0],
                            val[1], val[2]), x, curses.color_pair(2))
                total += float(held) * val[0]

    if y > len(coinl) + 3:
        stdscr.addnstr(y - 2, 0, 'Total Holdings: {:10.2f}    '
            .format(total), x, curses.color_pair(3))
        stdscr.addnstr(y - 1, 0,
            '[A] Add coin or update value [R] Remove coin [0\Q]Exit', x,
            curses.color_pair(2))

def read_wallet():
    ''' Reads the wallet data from its json file '''
    try:
        with open(datafile, 'r') as f:
            return json.load(f)
    except (FileNotFoundError, ValueError):
        # missing or malformed wallet
        write_wallet({})
        return {}

def write_wallet(wallet):
    ''' Reads the wallet data to its json file '''
    with open(datafile, 'w') as f:
        json.dump(wallet, f)

def get_string(stdscr, prompt):
    '''Requests and string from the user'''
    stdscr.nodelay(0)
    curses.echo()
    stdscr.clear()
    stdscr.addnstr(0, 0, prompt, -1, curses.color_pair(2))
    curses.curs_set(1)
    stdscr.refresh()
    in_str = stdscr.getstr(1, 0, 20).decode()
    curses.noecho()
    curses.curs_set(0)
    stdscr.clear()
    stdscr.nodelay(1)
    return in_str

def add_coin(coin_amount, wallet):
    ''' Remove a coin and its amount to the wallet '''
    if not p.match(coin_amount):
        return wallet

    coin, amount = coin_amount.split(',')
    wallet[coin] = amount

    return wallet

def remove_coin(coin, wallet):
    ''' Remove a coin and its amount from the wallet '''
    # coin = '' if window is resized while waiting for string
    if coin:
        return wallet.pop(coin, None)
    return wallet

def mainc(stdscr):
    inp = 0
    wallet = read_wallet()
    y, x = stdscr.getmaxyx()
    conf_scr()
    stdscr.bkgd(' ', curses.color_pair(2))
    stdscr.clear()
    stdscr.nodelay(1)
    while inp != 48 and inp != 27 and inp != 81 and inp != 113:
        while True:
            try:
                write_scr(stdscr, wallet, y, x)
            except curses.error:
                pass

            inp = stdscr.getch()
            if inp != curses.KEY_RESIZE:
                break
            stdscr.erase()
            y, x = stdscr.getmaxyx()

        if inp == 97 or inp == 65:
            if y > 2:
                data = get_string(stdscr,
                    'Enter in format Symbol,Amount e.g. BTC,10')
                wallet = add_coin(data, wallet)

        if inp == 82 or inp == 114:
            if y > 2:
                data = get_string(stdscr,
                    'Enter the symbol of coin to be removed, e.g. BTC')
                wallet = remove_coin(data, wallet)

    write_wallet(wallet)

def main():
    if os.path.isfile(basedir):
        sys.exit('Please remove your old configuration file at {}'.format(basedir))
    os.makedirs(basedir, exist_ok=True)

    global config
    config = read_configuration(conffile)

    requests_cache.install_cache(cache_name='api_cache', backend='memory',
        expire_after=int(config['api'].get('cache', 10)))

    curses.wrapper(mainc)

if __name__ == "__main__":
    main()
<commit_msg>Uppercase globals as per PEP8<commit_after>import curses
import os
import sys
import re
import shutil
import configparser
import json

import requests
import requests_cache

# GLOBALS!
BASEDIR = os.path.join(os.path.expanduser('~'), '.cryptop')
DATAFILE = os.path.join(BASEDIR, 'wallet.json')
CONFFILE = os.path.join(BASEDIR, 'config.ini')
CONFIG = configparser.ConfigParser()
COIN_FORMAT = re.compile('[A-Z]{2,5},\d{0,}\.?\d{0,}')

def read_configuration(confpath):
    # copy our default config file
    if not os.path.isfile(confpath):
        defaultconf = os.path.join(os.path.dirname(__file__), 'config.ini')
        shutil.copyfile(defaultconf, CONFFILE)

    CONFIG.read(confpath)
    return CONFIG

def if_coin(coin, url='https://www.cryptocompare.com/api/data/coinlist/'):
    '''Check if coin exists'''
    return coin in requests.get(url).json()['Data']

def get_price(coin, curr=None):
    '''Get the data on coins'''
    curr = curr or CONFIG['api'].get('currency', 'USD')
    fmt = 'https://min-api.cryptocompare.com/data/pricemultifull?fsyms={}&tsyms={}'

    try:
        r = requests.get(fmt.format(coin, curr))
    except requests.exceptions.RequestException:
        sys.exit('Could not complete request')

    try:
        data_raw = r.json()['RAW']
        return [(data_raw[c][curr]['PRICE'],
                data_raw[c][curr]['HIGH24HOUR'],
                data_raw[c][curr]['LOW24HOUR']) for c in coin.split(',')]
    except:
        sys.exit('Could not parse data')

def get_theme_colors():
    ''' Returns curses colors according to the config'''
    def get_curses_color(name_or_value):
        try:
            return getattr(curses, 'COLOR_' + name_or_value.upper())
        except AttributeError:
            return int(name_or_value)

    theme_config = CONFIG['theme']
    return (get_curses_color(theme_config.get('text', 'yellow')),
        get_curses_color(theme_config.get('banner', 'yellow')),
        get_curses_color(theme_config.get('banner_text', 'black')),
        get_curses_color(theme_config.get('background', -1)))

def conf_scr():
    '''Configure the screen and colors/etc'''
    curses.curs_set(0)
    curses.start_color()
    curses.use_default_colors()
    text, banner, banner_text, background = get_theme_colors()
    curses.init_pair(2, text, background)
    curses.init_pair(3, banner_text, banner)

def write_scr(stdscr, wallet, y, x):
    '''Write text and formatting to screen'''
    if y >= 1:
        stdscr.addnstr(0, 0, 'cryptop v0.1.2', x, curses.color_pair(2))
    if y >= 2:
        header = '  COIN      PRICE          HELD        VAL     HIGH      LOW  '
        stdscr.addnstr(1, 0, header, x, curses.color_pair(3))

    total = 0
    coinl = list(wallet.keys())
    heldl = list(wallet.values())
    if coinl:
        coinvl = get_price(','.join(coinl))

        if y > 3:
            for coin, val, held in zip(coinl, coinvl, heldl):
                if coinl.index(coin) + 2 < y:
                    stdscr.addnstr(coinl.index(coin) + 2, 0,
                        '  {:<5}  {:8.2f} {:>13.8f} {:10.2f} {:8.2f} {:8.2f}'
                        .format(coin, val[0], float(held), float(held) * val[0],
                            val[1], val[2]), x, curses.color_pair(2))
                total += float(held) * val[0]

    if y > len(coinl) + 3:
        stdscr.addnstr(y - 2, 0, 'Total Holdings: {:10.2f}    '
            .format(total), x, curses.color_pair(3))
        stdscr.addnstr(y - 1, 0,
            '[A] Add coin or update value [R] Remove coin [0\Q]Exit', x,
            curses.color_pair(2))

def read_wallet():
    ''' Reads the wallet data from its json file '''
    try:
        with open(DATAFILE, 'r') as f:
            return json.load(f)
    except (FileNotFoundError, ValueError):
        # missing or malformed wallet
        write_wallet({})
        return {}

def write_wallet(wallet):
    ''' Reads the wallet data to its json file '''
    with open(DATAFILE, 'w') as f:
        json.dump(wallet, f)

def get_string(stdscr, prompt):
    '''Requests and string from the user'''
    stdscr.nodelay(0)
    curses.echo()
    stdscr.clear()
    stdscr.addnstr(0, 0, prompt, -1, curses.color_pair(2))
    curses.curs_set(1)
    stdscr.refresh()
    in_str = stdscr.getstr(1, 0, 20).decode()
    curses.noecho()
    curses.curs_set(0)
    stdscr.clear()
    stdscr.nodelay(1)
    return in_str

def add_coin(coin_amount, wallet):
    ''' Remove a coin and its amount to the wallet '''
    if not COIN_FORMAT.match(coin_amount):
        return wallet

    coin, amount = coin_amount.split(',')
    wallet[coin] = amount

    return wallet

def remove_coin(coin, wallet):
    ''' Remove a coin and its amount from the wallet '''
    # coin = '' if window is resized while waiting for string
    if coin:
        return wallet.pop(coin, None)
    return wallet

def mainc(stdscr):
    inp = 0
    wallet = read_wallet()
    y, x = stdscr.getmaxyx()
    conf_scr()
    stdscr.bkgd(' ', curses.color_pair(2))
    stdscr.clear()
    stdscr.nodelay(1)
    while inp != 48 and inp != 27 and inp != 81 and inp != 113:
        while True:
            try:
                write_scr(stdscr, wallet, y, x)
            except curses.error:
                pass

            inp = stdscr.getch()
            if inp != curses.KEY_RESIZE:
                break
            stdscr.erase()
            y, x = stdscr.getmaxyx()

        if inp == 97 or inp == 65:
            if y > 2:
                data = get_string(stdscr,
                    'Enter in format Symbol,Amount e.g. BTC,10')
                wallet = add_coin(data, wallet)

        if inp == 82 or inp == 114:
            if y > 2:
                data = get_string(stdscr,
                    'Enter the symbol of coin to be removed, e.g. BTC')
                wallet = remove_coin(data, wallet)

    write_wallet(wallet)

def main():
    if os.path.isfile(BASEDIR):
        sys.exit('Please remove your old configuration file at {}'.format(BASEDIR))
    os.makedirs(BASEDIR, exist_ok=True)

    global CONFIG
    CONFIG = read_configuration(CONFFILE)

    requests_cache.install_cache(cache_name='api_cache', backend='memory',
        expire_after=int(CONFIG['api'].get('cache', 10)))

    curses.wrapper(mainc)

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


<commit_before>#!/usr/bin/python
"""Classes for working with homographies easily."""

import os, sys, time, argparse, traceback
import ast
import numpy as np
import multiprocessing, Queue
import cvgui, cvgeom
import cv2

class Homography(object):
    """A class containing a homography computed from a set of point
       correspondences taken from an aerial image and a video frame."""
    # TODO split this up into static/class method(s) to clean it up
    def __init__(self, aerialPoints=None, cameraPoints=None, unitsPerPixel=1.0, homographyFilename=None, worldPoints=None, homography=None, videoWidth=None, videoHeight=None):
        self.aerialPoints = cvgeom.ObjectCollection(aerialPoints) if aerialPoints is not None else aerialPoints
        self.cameraPoints = cvgeom.ObjectCollection(cameraPoints) if cameraPoints is not None else cameraPoints
        self.worldPoints = cvgeom.ObjectCollection(worldPoints) if worldPoints is not None else worldPoints
        self.unitsPerPixel = unitsPerPixel
        self.homographyFilename = None if homographyFilename is not None and not os.path.exists(homographyFilename) else homographyFilename
        self.videoWidth = videoWidth
        self.videoHeight = videoHeight

        self.worldPts = None
        self.cameraPts = None
        self.homography = np.loadtxt(self.homographyFilename) if self.homographyFilename is not None else homography
        self.inverted = None
        self.mask = None
        self.worldPointDists = None
        self.worldPointSquareDists = None
        self.worldPointError = None

        if self.homography is not None:
            self.invert()

    @staticmethod
    def fromString(s, **kwargs):
        """Load a homography from a string (like [[a,b,c],[d,e,f],[g,h,i]]),
           for instance from a configuration file."""
        return Homography(homography=ast.literal_eval(s), **kwargs)

    @staticmethod
    def fromArray(homArray, **kwargs):
        """
        Construct the homography object from a numpy array.
        """
        return Homography(homography=homArray, **kwargs)

    @staticmethod
    def getObjColFromArray(pArray):
        """Get an ObjectCollection of points from a 2xN array."""
        d = cvgeom.ObjectCollection()
        i = 1
        for x, y in zip(*pArray):
            d[i] = cvgeom.imagepoint(x, y, index=i)
            i += 1
        return d

    @staticmethod
    def getPointArray(points):
        """Get an Nx2 floating-point numpy array from an ObjectCollection of points,
           or a single point."""
        a = []
        if isinstance(points, cvgeom.ObjectCollection):
            for i in sorted(points.keys()):
                a.append(points[i].asTuple())
        elif isinstance(points, cvgeom.imagepoint):     # wrap in a list if only one point
            a = [points.asTuple()]
        else:
            # maybe they gave us an array, list, etc.
            # we will know soon
            a = points
        return np.array(a, dtype=np.float64)

    @staticmethod
    def invertHomography(homography):
        invH = np.linalg.inv(homography)
        invH /= invH[2,2]
        return invH

    def getWorldGrid(self):
        """
        Get an array of points that represent all possible world space coordinates that
        can be represented in the camera frame give its resolution.
        """
        if all([self.videoWidth, self.videoHeight, self.homography is not None]):
            # make a meshgrid of all possible coordinates in the camera frame
            mg = np.mgrid[0:self.videoHeight,0:self.videoWidth]

            # project the pixel coordinates to world space
            return self.projectPointArray(mg.reshape(2,-1)).reshape(2,self.videoHeight,self.videoWidth)

    def getMaxValue(self):
        """
        Determine the maximum position value in world units that can be measured in
        the camera frame.
        """
        wgp = self.getWorldGrid()
        if wgp is not None:
            return np.max(wgp)

    def computePrecision(self):
        """
        Compute the precision allowed by the camera frame in world units. Returns the
        value of the smallest change that can be represented in the image, calculated
        as the smallest distance in world space between any two neighboring pixels in
        the camera frame.
        """
        wgp = self.getWorldGrid()
        if wgp is not None:

            # calculate distance to the 3 next points (i,j+1), (i+1, j), and (i+1,j+1)
            # distance to point down
            wgup = wgp[:,0:-1,:]
            wgdown = wgp[:,1:,:]
            wgudsq = (wgup-wgdown)**2
            udDistMin = np.min(np.sqrt(wgudsq[0]+wgudsq[1]))

            # distance to point to right
            wgleft = wgp[:,:,0:-1]
            wgright = wgp[:,:,1:]
            wglrsq = (wgleft-wgright)**2
            lrDistMin = np.min(np.sqrt(wglrsq[0]+wglrsq[1]))

            # distance to diagonal point
            wgo = wgp[:,0:-1,0:-1]
            wgdiag = wgp[:,1:,1:]
            wgdiagsq = (wgo-wgdiag)**2
            diagDistMin = np.min(np.sqrt(wgdiagsq[0]+wgdiagsq[1]))

            return min(udDistMin, lrDistMin, diagDistMin)

    def toString(self):
        if self.homography is not None:
            return str([list(h) for h in self.homography])

    def savetxt(self, filename):
        if self.homography is not None:
            np.savetxt(filename, self.homography)

    def projectPointArray(self, points, invert=False):
        if len(points) > 0:
            augmentedPoints = np.append(points,[[1]*points.shape[1]], 0)
            hom = self.inverted if invert else self.homography
            prod = np.dot(hom, augmentedPoints)
            return prod[0:2]/prod[2]
        else:
            return np.array([], dtype=np.float64)

    def findHomography(self):
        """Compute the homography from the two sets of points and the given units."""
        if self.aerialPoints is not None:
            self.worldPts = self.unitsPerPixel*Homography.getPointArray(self.aerialPoints)
        elif self.worldPoints is not None:
            self.worldPts = Homography.getPointArray(self.worldPoints)
        self.cameraPts = Homography.getPointArray(self.cameraPoints)
        self.homography, self.mask = cv2.findHomography(self.cameraPts, self.worldPts)
        self.invert()

    def invert(self):
        if self.homography is not None:
            self.inverted = Homography.invertHomography(self.homography)

    def projectToAerial(self, points, objCol=True):
        """Project points from image space to the aerial image (without units) for plotting."""
        if self.homography is not None:
            pts = self.projectPointArray(Homography.getPointArray(points).T)/self.unitsPerPixel
            pts = Homography.getObjColFromArray(pts) if objCol else pts
            return pts

    def projectToWorld(self, points, objCol=True):
        """Project an ObjectCollection of points in video space to world
           space (in units of unitsPerPixel) using the homography."""
        if self.homography is not None:
            pts = self.projectPointArray(Homography.getPointArray(points).T)
            pts = Homography.getObjColFromArray(pts) if objCol else pts
            return pts

    def projectToImage(self, points, fromAerial=True, objCol=True):
        """Project an ObjectCollection of points from aerial or world space to image space."""
        if self.homography is not None:
            pArray = Homography.getPointArray(points).T
            ipts = pArray*self.unitsPerPixel if fromAerial else pArray
            pts = self.projectPointArray(ipts, invert=True)
            pts = Homography.getObjColFromArray(pts) if objCol else pts
            return pts

    def calculateError(self, squared=True):
        """Calculate the error (average of distances (squared, by default) between corresponding points) of
           the homography in world units."""
        # take the aerial points and calculate their position in world coordinates (i.e. multiply by unitsPerPixel)
        worldPts = Homography.getPointArray(self.aerialPoints)*self.unitsPerPixel

        # project the camera points to world coordinates with projectToWorld
        projWorldPts = self.projectToWorld(self.cameraPoints, objCol=False)

        # calculate the error ([squared] distance) between each pair of points
        self.worldPointDists = np.sqrt(np.sum((worldPts-projWorldPts)**2, axis=1))       # calculate the distance between corresponding points
        self.worldPointSquareDists = self.worldPointDists**2

        # average the error values (not summed, so we don't penalize picking more points)
        err = np.sum(self.worldPointSquareDists) if squared else np.sum(self.worldPointDists)
        self.worldPointError = err/len(self.aerialPoints)
        return self.worldPointError
    <commit_msg>Minor modifications and added notes to describe affine homography transformation for projecting between coordinate spaces<commit_after>#!/usr/bin/python
"""Classes for working with homographies easily."""

import os, sys, time, argparse, traceback
import ast
import numpy as np
import multiprocessing, Queue
import cvgui, cvgeom
import cv2

class Homography(object):
    """
    A class containing a homography computed from a set of point
    correspondences taken from an aerial image and a video frame.

    For more information, see the following:
        http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html?highlight=findhomography
        https://en.wikipedia.org/wiki/Homography_(computer_vision)
        https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations
    """
    # TODO split this up into static/class method(s) to clean it up
    def __init__(self, aerialPoints=None, cameraPoints=None, unitsPerPixel=1.0, homographyFilename=None, worldPoints=None, homography=None, videoWidth=None, videoHeight=None):
        self.aerialPoints = cvgeom.ObjectCollection(aerialPoints) if aerialPoints is not None else aerialPoints
        self.cameraPoints = cvgeom.ObjectCollection(cameraPoints) if cameraPoints is not None else cameraPoints
        self.worldPoints = cvgeom.ObjectCollection(worldPoints) if worldPoints is not None else worldPoints
        self.unitsPerPixel = unitsPerPixel
        self.homographyFilename = None if homographyFilename is not None and not os.path.exists(homographyFilename) else homographyFilename
        self.videoWidth = videoWidth
        self.videoHeight = videoHeight

        self.worldPts = None
        self.cameraPts = None
        self.homography = np.loadtxt(self.homographyFilename) if self.homographyFilename is not None else homography
        self.inverted = None
        self.mask = None
        self.worldPointDists = None
        self.worldPointSquareDists = None
        self.worldPointError = None

        if self.homography is not None:
            self.invert()

    @classmethod
    def fromString(cls, s, **kwargs):
        """Load a homography from a string (like [[a,b,c],[d,e,f],[g,h,i]]),
           for instance from a configuration file."""
        return cls(homography=ast.literal_eval(s), **kwargs)

    @classmethod
    def fromArray(cls, homArray, **kwargs):
        """
        Construct the homography object from a numpy array.
        """
        return cls(homography=homArray, **kwargs)

    @classmethod
    def getObjColFromArray(cls, pArray):
        """Get an ObjectCollection of points from a 2xN array."""
        d = cvgeom.ObjectCollection()
        i = 1
        for x, y in zip(*pArray):
            d[i] = cvgeom.imagepoint(x, y, index=i)
            i += 1
        return d

    @classmethod
    def getPointArray(cls, points):
        """Get an Nx2 floating-point numpy array from an ObjectCollection of points,
           or a single point."""
        a = []
        if isinstance(points, dict):
            for i in sorted(points.keys()):
                a.append(points[i].asTuple())
        elif isinstance(points, list) and len(points) > 0 and isinstance(points[0], cvgeom.imagepoint):
            for p in points:
                a.append(p.asTuple())
        elif isinstance(points, cvgeom.imagepoint):     # wrap in a list if only one point
            a = [points.asTuple()]
        elif isinstance(points, tuple) and len(points) == 2:
            # probably a single point represented as an (x,y) tuple - just pack it in a list
            a = [points]
        else:
            # maybe they gave us an array, list, etc.
            # we will know soon
            a = points
        return np.array(a, dtype=np.float32)

    @classmethod
    def invertHomography(cls, homography):
        invH = np.linalg.inv(homography)
        invH /= invH[2,2]
        return invH

    def getWorldGrid(self):
        """
        Get an array of points that represent all possible world space coordinates that
        can be represented in the camera frame give its resolution.
        """
        if all([self.videoWidth, self.videoHeight, self.homography is not None]):
            # make a meshgrid of all possible coordinates in the camera frame
            mg = np.mgrid[0:self.videoHeight,0:self.videoWidth]

            # project the pixel coordinates to world space
            return self.projectPointArray(mg.reshape(2,-1).T).reshape(2,self.videoHeight,self.videoWidth)

    def getMaxValue(self):
        """
        Determine the maximum position value in world units that can be measured in
        the camera frame.
        """
        wgp = self.getWorldGrid()
        if wgp is not None:
            return np.max(wgp)

    def computePrecision(self):
        """
        Compute the precision allowed by the camera frame in world units. Returns the
        value of the smallest change that can be represented in the image, calculated
        as the smallest distance in world space between any two neighboring pixels in
        the camera frame.
        """
        wgp = self.getWorldGrid()
        if wgp is not None:

            # calculate distance to the 3 next points (i,j+1), (i+1, j), and (i+1,j+1)
            # distance to point down
            wgup = wgp[:,0:-1,:]
            wgdown = wgp[:,1:,:]
            wgudsq = (wgup-wgdown)**2
            udDistMin = np.min(np.sqrt(wgudsq[0]+wgudsq[1]))

            # distance to point to right
            wgleft = wgp[:,:,0:-1]
            wgright = wgp[:,:,1:]
            wglrsq = (wgleft-wgright)**2
            lrDistMin = np.min(np.sqrt(wglrsq[0]+wglrsq[1]))

            # distance to diagonal point
            wgo = wgp[:,0:-1,0:-1]
            wgdiag = wgp[:,1:,1:]
            wgdiagsq = (wgo-wgdiag)**2
            diagDistMin = np.min(np.sqrt(wgdiagsq[0]+wgdiagsq[1]))

            return min(udDistMin, lrDistMin, diagDistMin)

    def toString(self):
        if self.homography is not None:
            return str([list(h) for h in self.homography])

    def savetxt(self, filename):
        if self.homography is not None:
            np.savetxt(filename, self.homography)

    def projectPointArray(self, points, invert=False):
        """
        Project the N x 2 matrix of points using our homography. This involves converting
        the points from Cartesian coordinates to homogeneous coordinates, which makes
        translations linearly independent, allowing us to consider the transformation as
        an affine transformation and compute the projection using matrix multiplication.
        """
        nPoints = points.shape[0]
        if nPoints > 0:
            # convert points to homogeneous coordinates by setting w (the z-axis) to 1
            # the alternate way to do this is: np.append(points.T,[[1]*points.T.shape[1]], 0)
            homogeneousCoords = cv2.convertPointsToHomogeneous(points)[:,0,:].T

            # invert the homography if we are projecting from world space to image space
            hom = self.inverted if invert else self.homography

            # perform perspective transformation (affine, so we can ignore the w component we set to 1)
            # matrix multiplication of homography x homogeneousCoords
            prod = np.dot(hom, homogeneousCoords)

            # convert homogeneous coordinates back into Cartesian
            # TODO this should work: projPoints = cv2.convertPointsFromHomogeneous(np.float32(prod.reshape(3,1,nPoints)))[:,0,:]
            projPoints = prod[0:2]/prod[2]
            return projPoints
        else:
            return np.array([], dtype=np.float32)

    def findHomography(self):
        """
        Compute the homography from the two sets of points and the given units.

        For more details, see:
            http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html?highlight=findhomography
            https://en.wikipedia.org/wiki/Homography_(computer_vision)
            https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations
        """
        if self.aerialPoints is not None:
            self.worldPts = self.unitsPerPixel*self.getPointArray(self.aerialPoints)
        elif self.worldPoints is not None:
            self.worldPts = self.getPointArray(self.worldPoints)
        self.cameraPts = self.getPointArray(self.cameraPoints)
        self.homography, self.mask = cv2.findHomography(self.cameraPts, self.worldPts)
        self.invert()

    def invert(self):
        if self.homography is not None:
            self.inverted = self.invertHomography(self.homography)

    def projectToAerial(self, points, objCol=True):
        """Project points from image space to the aerial image (without units) for plotting."""
        if self.homography is not None:
            pts = self.projectPointArray(self.getPointArray(points))/self.unitsPerPixel
            pts = self.getObjColFromArray(pts) if objCol else pts
            return pts

    def projectToWorld(self, points, objCol=True):
        """Project an ObjectCollection of points in video space to world
           space (in units of unitsPerPixel) using the homography."""
        if self.homography is not None:
            pts = self.projectPointArray(self.getPointArray(points))
            pts = self.getObjColFromArray(pts) if objCol else pts
            return pts

    def projectToImage(self, points, fromAerial=True, objCol=True):
        """Project an ObjectCollection of points from aerial or world space to image space."""
        if self.homography is not None:
            pArray = self.getPointArray(points)
            ipts = pArray*self.unitsPerPixel if fromAerial else pArray
            pts = self.projectPointArray(ipts, invert=True)
            pts = self.getObjColFromArray(pts) if objCol else pts
            return pts

    def calculateError(self, squared=True):
        """Calculate the error (average of distances (squared, by default) between corresponding points) of
           the homography in world units."""
        # take the aerial points and calculate their position in world coordinates (i.e. multiply by unitsPerPixel)
        worldPts = self.getPointArray(self.aerialPoints)*self.unitsPerPixel

        # project the camera points to world coordinates with projectToWorld
        projWorldPts = self.projectToWorld(self.cameraPoints, objCol=False)

        # calculate the error ([squared] distance) between each pair of points
        self.worldPointDists = np.sqrt(np.sum((worldPts-projWorldPts)**2, axis=1))       # calculate the distance between corresponding points
        self.worldPointSquareDists = self.worldPointDists**2

        # average the error values (not summed, so we don't penalize picking more points)
        err = np.sum(self.worldPointSquareDists) if squared else np.sum(self.worldPointDists)
        self.worldPointError = err/len(self.aerialPoints)
        return self.worldPointError
    <|endoftext|>


<commit_before>#!/usr/bin/python
import os
import random

## BSD-3
# (c) 2013 - Jonas Zetterberg
#
# Authors:
# * Jonas Zetterberg
# * Per Hallsmark

def Csv2Rep(csv):
	ret = ""
	status = {
		"Delivered"   : ["Completed", "Delivered", "Done with"],
		"Deleted"     : ["Done with"],
		"Completed"   : ["Completed", "Finished"],
		"In Progress" : ["Working on", "Working with", "Not done with"],
		"Not Started" : ["Retrived task", "New task", "Starting  with"],
		"On Hold"     : ["On hold", "Background working", "When I feel for it"],
	}
	internal = {
		"0" : ["external", "ericsson"],
		"1" : ["my", "internal"],
	}

	ASSIGNMENT= ["task", "assignment"]

	ADD_LINK=False

	rc = random.choice
	wl=0

	ret += "1) What tasks I worked on during this week?\n"
	for row in csv:
		if row[2] == "Status":
			continue # Skip first row FIXME: use as dict
		ret += " ".join(["    ",
			rc(status[row[2]]),
			rc(internal[row[3]]),
			rc(ASSIGNMENT),
			"to",
			row[5].lower()]
		)
		if ADD_LINK:
			ret += "(%s)" % row[4]
		else:
			ret += "."
		ret += "\n"
		if row[2] == "In Progress":
			wl = wl+1;

	ret += "2) Anything preventing me to accomplish the tasks?\n"
	ret += "     Not that I know of.\n"

	ret += "3) My workload (I am working more than normal time? Less?)\n"
	if wl <= 4:
		ret += "     Normal workload\n"
	elif wl <= 6:
		ret += "     High workload\n"
	elif wl <= 8:
		ret += "     Very high workload\n"
	else:
		ret += "     I am hitting the wall!\n"

	ret += "4) Any new opportunity for business I have heard?\n"
	if wl < 5:
		ret += "     No\n"
	else:
		ret += "     There seems to be a need for more consultants at my workplace"
	return ret
<commit_msg>Add Blocked state<commit_after>#!/usr/bin/python
import os
import random

## BSD-3
# (c) 2013 - Jonas Zetterberg
#
# Authors:
# * Jonas Zetterberg
# * Per Hallsmark

def Csv2Rep(csv):
	ret = ""
	status = {
		"Delivered"   : ["Completed", "Delivered", "Done with"],
		"Blocked"     : ["Blocked", "Could not work with"],
		"Deleted"     : ["Done with"],
		"Completed"   : ["Completed", "Finished"],
		"In Progress" : ["Working on", "Working with", "Not done with"],
		"Not Started" : ["Retrived task", "New task", "Starting  with"],
		"On Hold"     : ["On hold", "Background working", "When I feel for it"],
	}
	internal = {
		"0" : ["external", "ericsson"],
		"1" : ["my", "internal"],
	}

	ASSIGNMENT= ["task", "assignment"]

	ADD_LINK=False

	rc = random.choice
	wl=0

	ret += "1) What tasks I worked on during this week?\n"
	for row in csv:
		if row[2] == "Status":
			continue # Skip first row FIXME: use as dict
		ret += " ".join(["    ",
			rc(status[row[2]]),
			rc(internal[row[3]]),
			rc(ASSIGNMENT),
			"to",
			row[5].lower()]
		)
		if ADD_LINK:
			ret += "(%s)" % row[4]
		else:
			ret += "."
		ret += "\n"
		if row[2] == "In Progress":
			wl = wl+1;

	ret += "2) Anything preventing me to accomplish the tasks?\n"
	ret += "     Not that I know of.\n"

	ret += "3) My workload (I am working more than normal time? Less?)\n"
	if wl <= 4:
		ret += "     Normal workload\n"
	elif wl <= 6:
		ret += "     High workload\n"
	elif wl <= 8:
		ret += "     Very high workload\n"
	else:
		ret += "     I am hitting the wall!\n"

	ret += "4) Any new opportunity for business I have heard?\n"
	if wl < 5:
		ret += "     No\n"
	else:
		ret += "     There seems to be a need for more consultants at my workplace"
	return ret
<|endoftext|>


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

import sys
import os

import fsquery
import sendtoclipboard

def puaq():
    print("Usage: %s path" % os.path.basename(__file__))
    sys.exit(1)

def find_drive_letter(target_path):
    pos = target_path.find(":")
    if pos == -1:
        return None
    else:
        dl_path = os.path.join("/cygdrive", target_path[0:pos].lower())
        return dl_path

def cut_drive_letter(target_path):
    pos = target_path.find(":")
    if pos == -1:
        return None
    else:
        cut_path = target_path[pos+1:]
        return cut_path

def _has_key(map, item):

    try:
        if map[item] == True:
            return True
        else:
            return False
    except:
        return False

def linear_search(item, domain):

    amt = 0
    while (True):
        amt += 1
        if amt >= len(item):
            return None
        if _has_key(domain, item[0:amt]):
            return item[0:amt]

def _ar_to_map(in_ar):
    map = {}
    for it in in_ar:
        map[os.path.basename(it)] = True
    return map

def find_next_path(root, path_string):

    if not os.path.exists(root):
        return False, None

    contents = fsquery.makecontentlist(root, False, True, True, True, True, [])
    contents_map = _ar_to_map(contents)
    sr = linear_search(path_string, contents_map)
    if sr is None:
        return False, path_string
    else:
        return True, sr

def convert_win_path_to_cygwin_path(target_path):

    final_path = find_drive_letter(target_path)
    if final_path is None:
        return None
    target_path = cut_drive_letter(target_path)
    if target_path is None:
        return None

    while (True):
        result, next = find_next_path(final_path, target_path)
        target_path = target_path[len(next):]
        final_path = os.path.join(final_path, next)
        if not result:
            break

    return final_path

if __name__ == "__main__":

    target_path = ""
    if len(sys.argv) < 2:
        puaq()
    target_path = sys.argv[1]

    new_path = convert_win_path_to_cygwin_path(target_path)
    if new_path is not None:
        sendtoclipboard.sendtoclipboard(new_path)
<commit_msg>patched cygpath discovery script to work with paths that include spaces and halfway paths<commit_after>#!/usr/bin/env python3

import sys
import os

import fsquery
import sendtoclipboard

def puaq():
    print("Usage: %s path" % os.path.basename(__file__))
    sys.exit(1)

def find_drive_letter(target_path):
    pos = target_path.find(":")
    if pos == -1:
        return None
    else:
        dl_path = os.path.join("/cygdrive", target_path[0:pos].lower())
        return dl_path

def cut_drive_letter(target_path):
    pos = target_path.find(":")
    if pos == -1:
        return None
    else:
        cut_path = target_path[pos+1:]
        return cut_path

def _has_key(map, item):

    try:
        if map[item] == True:
            return True
        else:
            return False
    except:
        return False

def linear_search(item, domain):

    amt = 0
    while (True):
        amt += 1
        if amt >= len(item):
            return None
        if _has_key(domain, item[0:amt]):
            return item[0:amt]

def _ar_to_map(in_ar):
    map = {}
    for it in in_ar:
        map[os.path.basename(it)] = True
    return map

def find_next_path(root, path_string):

    if not os.path.exists(root):
        return False, None

    contents = fsquery.makecontentlist(root, False, True, True, True, True, [])
    contents_map = _ar_to_map(contents)
    sr = linear_search(path_string, contents_map)
    if sr is None:
        return False, path_string
    else:
        return True, sr

def convert_win_path_to_cygwin_path(target_path):

    final_path = find_drive_letter(target_path)
    if final_path is None:
        final_path = os.getcwd()
    else:
        target_path = cut_drive_letter(target_path)
    if target_path is None:
        return None

    while (True):
        result, next = find_next_path(final_path, target_path)
        target_path = target_path[len(next):]
        final_path = os.path.join(final_path, next)
        if not result:
            break

    return final_path

def merge_spaces(input):
    ret = ""
    for i in input:
        ret += i + " "
    return ret[0:len(ret)-1]

def escape_spaces(input):
    return input.replace(" ", "\\ ")

if __name__ == "__main__":

    target_path = ""
    if len(sys.argv) < 2:
        puaq()
    target_path = merge_spaces(sys.argv[1:])

    new_path = convert_win_path_to_cygwin_path(target_path)
    new_path = escape_spaces(new_path)
    if new_path is not None:
        sendtoclipboard.sendtoclipboard(new_path)
<|endoftext|>


<commit_before>#!/usr/bin/python
from   __future__             import print_function, unicode_literals
import argparse
from   datetime               import datetime
import email.charset
from   email.message          import Message
from   email.mime.application import MIMEApplication
from   email.mime.multipart   import MIMEMultipart
from   email.mime.text        import MIMEText
import locale
import os
import platform
import re
import signal
import socket
import subprocess
import sys
import traceback
from   daemon                 import DaemonContext  # python-daemon

if sys.version_info[0] == 2:
    from pipes import quote
else:
    from shlex import quote

__version__ = '0.1.0'

USER_AGENT = 'daemail {} ({} {})'.format(
    __version__, platform.python_implementation(), platform.python_version()
)

def subcmd(cmd, stdout=None, stderr=None):
    params = {}
    if stdout is None and stderr is None:
        params = {"stdout": subprocess.PIPE, "stderr": subprocess.STDOUT}
    else:
        if stdout:
            params["stdout"] = subprocess.PIPE
        if stderr:
            params["stderr"] = subprocess.PIPE
    start = datetime.now()
    p = subprocess.Popen(cmd, **params)
    # The command's output is all going to be in memory at some point anyway,
    # so why not start with `communicate`?
    out, err = p.communicate()
    end = datetime.now()
    return {
        "rc": p.returncode,
        "start": start.isoformat(),
        "end": end.isoformat(),
        "stdout": out,
        "stderr": err,
        "pid": p.pid,
        "argv0": cmd[0],
        "command": ' '.join(map(quote, cmd)),
    }

def mail_quote(s):
    return re.sub(r'^(?=.)', '> ', s, flags=re.M | re.S)

def main():
    prefenc = locale.getpreferredencoding(False)
    parser = argparse.ArgumentParser()
    parser.add_argument('-e', '--encoding', default=prefenc,
                        help='Set encoding of stdout and stderr')
    parser.add_argument('-E', '--err-encoding', help='Set encoding of stderr',
                        metavar='ENCODING')
    parser.add_argument('-f', '--from', dest='sender',
                        help='From: address of e-mail')
    parser.add_argument('-F', '--failed', action='store_true',
                        help='Only send e-mail if command returned nonzero')
    parser.add_argument('-l', '--logfile',
                        help='Append unrecoverable errors to this file')
    parser.add_argument('-m', '--mail-cmd', default='sendmail -t',
                        metavar='COMMAND', help='Command for sending e-mail')
    parser.add_argument('-n', '--nonempty', action='store_true',
                        help='Only send e-mail if there was output')
    parser.add_argument('--no-stdout', action='store_true',
                        help="Don't capture stdout")
    parser.add_argument('--no-stderr', action='store_true',
                        help="Don't capture stderr")
    parser.add_argument('--split', action='store_true',
                        help='Capture stdout and stderr separately')
    parser.add_argument('-t', '--to', help='To: address of e-mail')
    parser.add_argument('-V', '--version', action='version',
                                           version='daemail ' + __version__)
    parser.add_argument('command')
    parser.add_argument('args', nargs=argparse.REMAINDER)
    args = parser.parse_args()
    if args.sender is None:
        args.sender = os.getlogin() + '@' + socket.gethostname()
    if args.to is None:
        args.to = os.getlogin() + '@' + socket.gethostname()
    errhead = 'Error daemonizing'
    try:
        with DaemonContext(working_directory=os.getcwd()):
            outopts = {}
            if args.split or args.no_stdout or args.no_stderr:
                outopts = {
                    "stdout": not args.no_stdout,
                    "stderr": not args.no_stderr,
                }
            errhead = 'Error running command'
            proc = subcmd([args.command] + args.args, **outopts)
            if args.failed and proc["rc"] == 0 or \
                    args.nonempty and not (proc["stdout"] or proc["stderr"]):
                sys.exit(0)
            errhead = 'Error constructing e-mail'
            body = 'Start Time:  {start}\n' \
                   'End Time:    {end}\n' \
                   'Exit Status: {rc}'.format(**proc)
            if proc["rc"] < 0:
                # cf. <http://stackoverflow.com/q/2549939/744178>
                for k,v in vars(signal).items():
                    if k.startswith('SIG') and v == -proc["rc"]:
                        body += ' (' + k + ')'
                        break
            body += '\n'
            # An empty byte string is always an empty character string and vice
            # versa, right?
            attachments = []
            if proc["stdout"]:
                body += '\n'
                try:
                    stdout = proc["stdout"].decode(args.encoding)
                except UnicodeDecodeError:
                    stdout = MIMEApplication(proc["stdout"])
                    stdout.add_header('Content-Disposition', 'attachment',
                                      filename='stdout')
                    attachments.append(stdout)
                    body += 'The output could not be decoded and is attached.'
                else:
                    body += 'Output:\n' + mail_quote(stdout)
                body += '\n'
            elif proc["stdout"] == '':
                body += '\nOutput: none\n'
            if proc["stderr"]:
                # If stderr was captured separately but is still empty, don't
                # bother saying "Error Output: none".
                body += '\n'
                try:
                    proc["stderr"] = proc["stderr"].decode(args.err_encoding or
                                                           args.encoding)
                except UnicodeDecodeError:
                    stderr = MIMEApplication(proc["stderr"])
                    stderr.add_header('Content-Disposition', 'attachment',
                                      filename='stderr')
                    attachments.append(stderr)
                    body += 'The error output could not be decoded and is' \
                            ' attached.'
                else:
                    body += 'Error Output:\n' + mail_quote(proc["stderr"])
                body += '\n'
            chrset = email.charset.Charset('utf-8')
            chrset.body_encoding = email.charset.QP
            if attachments:
                msg = MIMEMultipart()
                txt = MIMEText('', _charset=None)
                txt.set_payload(body, chrset)
                msg.attach(txt)
                for a in attachments:
                    msg.attach(a)
            else:
                msg = Message()
                msg.set_payload(body, chrset)
            msg['Subject'] = ('[DONE]' if proc["rc"] == 0 else '[FAILED]') \
                           + ' ' + proc["command"]
            msg['From'] = args.sender
            msg['To'] = args.to
            msg['User-Agent'] = USER_AGENT
            errhead = 'Error sending e-mail'
            sendmail = subprocess.Popen(args.mail_cmd, shell=True,
                                        stdin=subprocess.PIPE)
            sendmail.communicate(str(msg))
            sys.exit(sendmail.returncode)
            ### TODO: Log an error if sendmail failed
    except Exception:
        if args.logfile:
            # If no logfile was specified or this open() fails, die alone where
            # no one will ever know.
            sys.stderr = open(args.logfile, 'a')
                ### What encoding do I use for this???
            print(datetime.now().isoformat(), errhead, file=sys.stderr)
            print('Command:', [args.command] + args.args, file=sys.stderr)
            print('From address:', args.sender, file=sys.stderr)
            print('To address:', args.to, file=sys.stderr)
            print('Mail command:', args.mail_cmd, file=sys.stderr)
            ### TODO: Also log whether stdout & stderr were being captured
            print('', file=sys.stderr)
            traceback.print_exc()

if __name__ == '__main__':
    main()
<commit_msg>Exit with failure if we failed<commit_after>#!/usr/bin/python
from   __future__             import print_function, unicode_literals
import argparse
from   datetime               import datetime
import email.charset
from   email.message          import Message
from   email.mime.application import MIMEApplication
from   email.mime.multipart   import MIMEMultipart
from   email.mime.text        import MIMEText
import locale
import os
import platform
import re
import signal
import socket
import subprocess
import sys
import traceback
from   daemon                 import DaemonContext  # python-daemon

if sys.version_info[0] == 2:
    from pipes import quote
else:
    from shlex import quote

__version__ = '0.1.0'

USER_AGENT = 'daemail {} ({} {})'.format(
    __version__, platform.python_implementation(), platform.python_version()
)

def subcmd(cmd, stdout=None, stderr=None):
    params = {}
    if stdout is None and stderr is None:
        params = {"stdout": subprocess.PIPE, "stderr": subprocess.STDOUT}
    else:
        if stdout:
            params["stdout"] = subprocess.PIPE
        if stderr:
            params["stderr"] = subprocess.PIPE
    start = datetime.now()
    p = subprocess.Popen(cmd, **params)
    # The command's output is all going to be in memory at some point anyway,
    # so why not start with `communicate`?
    out, err = p.communicate()
    end = datetime.now()
    return {
        "rc": p.returncode,
        "start": start.isoformat(),
        "end": end.isoformat(),
        "stdout": out,
        "stderr": err,
        "pid": p.pid,
        "argv0": cmd[0],
        "command": ' '.join(map(quote, cmd)),
    }

def mail_quote(s):
    return re.sub(r'^(?=.)', '> ', s, flags=re.M | re.S)

def main():
    prefenc = locale.getpreferredencoding(False)
    parser = argparse.ArgumentParser()
    parser.add_argument('-e', '--encoding', default=prefenc,
                        help='Set encoding of stdout and stderr')
    parser.add_argument('-E', '--err-encoding', help='Set encoding of stderr',
                        metavar='ENCODING')
    parser.add_argument('-f', '--from', dest='sender',
                        help='From: address of e-mail')
    parser.add_argument('-F', '--failed', action='store_true',
                        help='Only send e-mail if command returned nonzero')
    parser.add_argument('-l', '--logfile',
                        help='Append unrecoverable errors to this file')
    parser.add_argument('-m', '--mail-cmd', default='sendmail -t',
                        metavar='COMMAND', help='Command for sending e-mail')
    parser.add_argument('-n', '--nonempty', action='store_true',
                        help='Only send e-mail if there was output')
    parser.add_argument('--no-stdout', action='store_true',
                        help="Don't capture stdout")
    parser.add_argument('--no-stderr', action='store_true',
                        help="Don't capture stderr")
    parser.add_argument('--split', action='store_true',
                        help='Capture stdout and stderr separately')
    parser.add_argument('-t', '--to', help='To: address of e-mail')
    parser.add_argument('-V', '--version', action='version',
                                           version='daemail ' + __version__)
    parser.add_argument('command')
    parser.add_argument('args', nargs=argparse.REMAINDER)
    args = parser.parse_args()
    if args.sender is None:
        args.sender = os.getlogin() + '@' + socket.gethostname()
    if args.to is None:
        args.to = os.getlogin() + '@' + socket.gethostname()
    errhead = 'Error daemonizing'
    try:
        with DaemonContext(working_directory=os.getcwd()):
            outopts = {}
            if args.split or args.no_stdout or args.no_stderr:
                outopts = {
                    "stdout": not args.no_stdout,
                    "stderr": not args.no_stderr,
                }
            errhead = 'Error running command'
            proc = subcmd([args.command] + args.args, **outopts)
            if args.failed and proc["rc"] == 0 or \
                    args.nonempty and not (proc["stdout"] or proc["stderr"]):
                sys.exit(0)
            errhead = 'Error constructing e-mail'
            body = 'Start Time:  {start}\n' \
                   'End Time:    {end}\n' \
                   'Exit Status: {rc}'.format(**proc)
            if proc["rc"] < 0:
                # cf. <http://stackoverflow.com/q/2549939/744178>
                for k,v in vars(signal).items():
                    if k.startswith('SIG') and v == -proc["rc"]:
                        body += ' (' + k + ')'
                        break
            body += '\n'
            # An empty byte string is always an empty character string and vice
            # versa, right?
            attachments = []
            if proc["stdout"]:
                body += '\n'
                try:
                    stdout = proc["stdout"].decode(args.encoding)
                except UnicodeDecodeError:
                    stdout = MIMEApplication(proc["stdout"])
                    stdout.add_header('Content-Disposition', 'attachment',
                                      filename='stdout')
                    attachments.append(stdout)
                    body += 'The output could not be decoded and is attached.'
                else:
                    body += 'Output:\n' + mail_quote(stdout)
                body += '\n'
            elif proc["stdout"] == '':
                body += '\nOutput: none\n'
            if proc["stderr"]:
                # If stderr was captured separately but is still empty, don't
                # bother saying "Error Output: none".
                body += '\n'
                try:
                    proc["stderr"] = proc["stderr"].decode(args.err_encoding or
                                                           args.encoding)
                except UnicodeDecodeError:
                    stderr = MIMEApplication(proc["stderr"])
                    stderr.add_header('Content-Disposition', 'attachment',
                                      filename='stderr')
                    attachments.append(stderr)
                    body += 'The error output could not be decoded and is' \
                            ' attached.'
                else:
                    body += 'Error Output:\n' + mail_quote(proc["stderr"])
                body += '\n'
            chrset = email.charset.Charset('utf-8')
            chrset.body_encoding = email.charset.QP
            if attachments:
                msg = MIMEMultipart()
                txt = MIMEText('', _charset=None)
                txt.set_payload(body, chrset)
                msg.attach(txt)
                for a in attachments:
                    msg.attach(a)
            else:
                msg = Message()
                msg.set_payload(body, chrset)
            msg['Subject'] = ('[DONE]' if proc["rc"] == 0 else '[FAILED]') \
                           + ' ' + proc["command"]
            msg['From'] = args.sender
            msg['To'] = args.to
            msg['User-Agent'] = USER_AGENT
            errhead = 'Error sending e-mail'
            sendmail = subprocess.Popen(args.mail_cmd, shell=True,
                                        stdin=subprocess.PIPE)
            sendmail.communicate(str(msg))
            sys.exit(sendmail.returncode)
            ### TODO: Log an error if sendmail failed
    except Exception:
        if args.logfile:
            # If no logfile was specified or this open() fails, die alone where
            # no one will ever know.
            sys.stderr = open(args.logfile, 'a')
                ### What encoding do I use for this???
            print(datetime.now().isoformat(), errhead, file=sys.stderr)
            print('Command:', [args.command] + args.args, file=sys.stderr)
            print('From address:', args.sender, file=sys.stderr)
            print('To address:', args.to, file=sys.stderr)
            print('Mail command:', args.mail_cmd, file=sys.stderr)
            ### TODO: Also log whether stdout & stderr were being captured
            print('', file=sys.stderr)
            traceback.print_exc()
        sys.exit(1)

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


<commit_before>'''dataset: generate artificial data sets

SUMMARY OF APIs
bishop_ch1(num_samples)
d1(fun, num_samples, x_low, x_high, error_mean, error_variance)
outliers(num_features, num_unique_samples)
sparse(num_features, num_samples)
'''
from dataset_bishop_ch1 import bishop_ch1
from dataset_d1 import d1
from dataset_outliers import outliers
from dataset_sparse import sparse

if False:
    # use the imports, so as to avoid an error from pyflakes
    bishop_ch1()
    d1()
    outliers()
    sparse()
<commit_msg>documentation<commit_after>'''generate artificial data sets

SUMMARY OF APIs
bishop_ch1(num_samples)
d1(fun, num_samples, x_low, x_high, error_mean, error_variance)
outliers(num_features, num_unique_samples)
sparse(num_features, num_samples)
'''
from dataset_bishop_ch1 import bishop_ch1
from dataset_d1 import d1
from dataset_outliers import outliers
from dataset_sparse import sparse

if False:
    # use the imports, so as to avoid an error from pyflakes
    bishop_ch1()
    d1()
    outliers()
    sparse()
<|endoftext|>


<commit_before>"""This module contains function to get the necessary datasets and mlpython
problem for the project."""
from __future__ import print_function
import glob
import re
from scipy.misc import imread
from numpy import random
import numpy as np
from mlpython.mlproblems.classification import ClassificationProblem

def load_data(folder):
    """Load all the images files into memory. It puts them in a list of tuple containing the
    filename and the images. The images are in the same format as the file (rgb)."""
    folder.rstrip("/ ")
    filenames = glob.glob(folder + "/*.png")
    return ((imread(filename, flatten=True), filename) for filename in filenames)

def create_label(filename):
    """Uses a regular expression to find which objects corresponds to this filename and what is
    its orientation."""
    found = re.search(r'(\d{0,4}__\d{1,4})', filename).group(0).split('_')
    return int(found[0]), int(found[-1])

def create_labels(data):
    """Returns a generator which convert the filenames into (obj, orientation) tuple"""
    return ((image, create_label(filename)) for image, filename in data)

def divide_problem(data, seed=1234):
    random.seed(seed)
    indexes = list(range(0, 100))
    random.shuffle(indexes)
    train_indexes = indexes[0:30]
    train, test, valid, video = [], [], [], []
    for image, (sequence, angle) in data:
        if sequence in train_indexes:
            if angle in [0, 90, 180, 270]:
                train.append((image.ravel(), sequence))
            elif angle in [45, 135, 225, 315]:
                valid.append((image.ravel(), sequence))
            else:
                test.append((image.ravel(), sequence))
        else:
            video.append(image.ravel())
    return train, valid, test, video

def create_problem(data, seed=1234):
    train, valid, test, video_sequences = divide_problem(data)
    targets = set((i[1] for i in train))
    meta = {'targets':targets, 'input_size':len(train[0][0])}
    trainset = VideoClassification(data=train, video=video_sequences, metadata=meta)
    validset = VideoClassification(data=valid, video=video_sequences, metadata=meta)
    testset = VideoClassification(data=test, video=video_sequences, metadata=meta)
    return trainset, validset, testset

def get_classification_problem(img_folder):
    data = load_data(img_folder)
    data = create_labels(data)
    return create_problem(data, seed=1234)

class VideoClassification(ClassificationProblem):
    def __init__(self, data=None, video=None, metadata=None, seed=1234, call_setup=True):
        if metadata is None:
            metadata = {}
        ClassificationProblem.__init__(self, data=data, metadata=metadata, call_setup=call_setup)
        self.video = np.array(video)

    def getConsecutivesFrames(self, n=1):
        indices = random.randint(0, self.video.shape[0], (n, ))
        indices_next = indices + 1
        return self.video[indices, :], self.video[indices_next, :]

    def getNonConsecutivesFrames(self, n=1):
        indices = random.randint(0, self.video.shape[0], (n, ))
        indices_next = random.randint(0, self.video.shape[0], (n, ))
        return self.video[indices, :], self.video[indices_next, :]

if __name__ == "__main__":
    import time
    path = "../images/"
    data = load_data(path)
    #data = list(create_labels(data))
    data = create_labels(data)
    #t = time.time()
    train, valid, test = create_problem(data, seed=1234)
    #print(time.time() - t)
<commit_msg>Images 2d dans le dataset<commit_after>"""This module contains function to get the necessary datasets and mlpython
problem for the project."""
from __future__ import print_function
import glob
import re
from scipy.misc import imread
from numpy import random
import numpy as np
from mlpython.mlproblems.classification import ClassificationProblem

def load_data(folder):
    """Load all the images files into memory. It puts them in a list of tuple containing the
    filename and the images. The images are in the same format as the file (rgb)."""
    folder.rstrip("/ ")
    filenames = glob.glob(folder + "/*.png")
    return ((imread(filename, flatten=True), filename) for filename in filenames)

def create_label(filename):
    """Uses a regular expression to find which objects corresponds to this filename and what is
    its orientation."""
    found = re.search(r'(\d{0,4}__\d{1,4})', filename).group(0).split('_')
    return int(found[0]), int(found[-1])

def create_labels(data):
    """Returns a generator which convert the filenames into (obj, orientation) tuple"""
    return ((image, create_label(filename)) for image, filename in data)

def divide_problem(data, seed=1234):
    random.seed(seed)
    indexes = list(range(0, 100))
    random.shuffle(indexes)
    train_indexes = indexes[0:30]
    train, test, valid, video = [], [], [], []
    for image, (sequence, angle) in data:
        if sequence in train_indexes:
            if angle in [0, 90, 180, 270]:
                train.append((image, sequence))
            elif angle in [45, 135, 225, 315]:
                valid.append((image, sequence))
            else:
                test.append((image, sequence))
        else:
            video.append(image)
    np.random.shuffle(train)
    np.random.shuffle(test)
    np.random.shuffle(valid)
    return train, valid, test, video

def create_problem(data, seed=1234):
    train, valid, test, video_sequences = divide_problem(data)
    targets = set((i[1] for i in train))
    meta = {'targets':targets, 'input_size':len(train[0][0])}
    trainset = VideoClassification(data=train, video=video_sequences, metadata=meta)
    validset = VideoClassification(data=valid, video=video_sequences, metadata=meta)
    testset = VideoClassification(data=test, video=video_sequences, metadata=meta)
    return trainset, validset, testset

def get_classification_problem(img_folder):
    data = load_data(img_folder)
    data = create_labels(data)
    return create_problem(data, seed=1234)

class VideoClassification(ClassificationProblem):
    def __init__(self, data=None, video=None, metadata=None, seed=1234, call_setup=True):
        if metadata is None:
            metadata = {}
        ClassificationProblem.__init__(self, data=data, metadata=metadata, call_setup=call_setup)
        self.video = np.array(video)

    def getConsecutivesFrames(self, n=1):
        indices = random.randint(0, self.video.shape[0], (n, ))
        indices_next = indices + 1
        return self.video[indices, :], self.video[indices_next, :]

    def getNonConsecutivesFrames(self, n=1):
        indices = random.randint(0, self.video.shape[0], (n, ))
        indices_next = random.randint(0, self.video.shape[0], (n, ))
        return self.video[indices, :], self.video[indices_next, :]

if __name__ == "__main__":
    import time
    path = "../images/"
    data = load_data(path)
    #data = list(create_labels(data))
    data = create_labels(data)
    #t = time.time()
    train, valid, test = create_problem(data, seed=1234)
    #print(time.time() - t)
<|endoftext|>


<commit_before># coding:utf-8
from db.basic_db import db_session
from db.models import User
from decorators.decorator import db_commit_decorator

@db_commit_decorator
def save_users(users):
    db_session.add_all(users)
    db_session.commit()

@db_commit_decorator
def save_user(user):
    db_session.add(user)
    db_session.commit()

def get_user_by_uid(uid):
    return db_session.query(User).filter(User.uid == uid).first()
<commit_msg>add rollback when select error<commit_after># coding:utf-8
from db.models import User
from db.basic_db import db_session
from decorators.decorator import db_commit_decorator

@db_commit_decorator
def save_users(users):
    db_session.add_all(users)
    db_session.commit()

@db_commit_decorator
def save_user(user):
    db_session.add(user)
    db_session.commit()

@db_commit_decorator
def get_user_by_uid(uid):
    return db_session.query(User).filter(User.uid == uid).first()
<|endoftext|>


<commit_before>
import uuid

import paasmaker
from paasmaker.common.core import constants
from ..base import BaseJob
from paasmaker.util.plugin import MODE
from ...testhelpers import TestHelpers

import logging

import tornado
from pubsub import pub

import colander

# Root job: actual application delete (deletes the objects in the db)
# - service distributor: create jobs under the root for deleting each service
# - service a
# - service b
#
# Things this job doesn't do:
# - clean up packed files and source code. For now, leave this to a cleanup task
# - log files will expire eventually anyway
# - jobs will also expire
# - routing table will have already been dumped (because all instances have stopped before deletion)
# - routing stats: there's not yet a good way to export them

class ApplicationDeleteJobParametersSchema(colander.MappingSchema):
	application_id = colander.SchemaNode(
		colander.Integer(),
		title="Application ID",
		description="ID of the application to be deleted"
	)

class ApplicationDeleteRootJob(BaseJob):
	MODES = {
		MODE.JOB: ApplicationDeleteJobParametersSchema()
	}

	def start_job(self, context):
		session = self.configuration.get_database_session()
		application = session.query(
			paasmaker.model.Application
		).get(
			self.parameters["application_id"]
		)

		if application is None:
			error_msg = "Can't find application of id %d to delete" % self.parameters["application_id"]
			self.logger.error(error_msg)
			self.failed(error_msg)
			return

		session.delete(application)
		session.commit()

		self.success({}, "Deleted application id %d" % self.parameters["application_id"])

	@classmethod
	def setup_for_application(cls, configuration, application, callback):
		tags = [
			'workspace:%d' % application.workspace.id,
			'application:%d' % application.id
		]

		tree = configuration.job_manager.get_specifier()
		tree.set_job(
			'paasmaker.job.application.delete',
			{
				'application_id': application.id
			},
			"Delete application %s" % application.name,
			tags=tags
		)

		for service in application.services:
			servicejob = tree.add_child()
			servicejob.set_job(
				'paasmaker.job.application.delete.service',
				{
					'service_id': service.id
				},
				"Delete service '%s'" % service.name
			)

		def on_tree_added(root_id):
			callback(root_id)

		# Add that entire tree into the job manager.
		configuration.job_manager.add_tree(tree, on_tree_added)

class ApplicationDeleteServiceParametersSchema(colander.MappingSchema):
	service_id = colander.SchemaNode(
		colander.Integer(),
		title="Service ID",
		description="ID of the service to be deleted"
	)

class ApplicationDeleteServiceJob(BaseJob):
	MODES = {
		MODE.JOB: ApplicationDeleteServiceParametersSchema()
	}

	def start_job(self, context):
		self.session = self.configuration.get_database_session()
		self.service = self.session.query(
			paasmaker.model.Service
		).get(
			self.parameters['service_id']
		)

		plugin_exists = self.configuration.plugins.exists(
			self.service.provider,
			paasmaker.util.plugin.MODE.SERVICE_DELETE
		)

		if not plugin_exists:
			self.service.state = constants.SERVICE.ERROR
			self.session.add(self.service)
			self.session.commit()
			self.failed("Plugin with mode SERVICE_DELETE doesn't exist for service %s" %self.service.provider)
			return

		service_plugin = self.configuration.plugins.instantiate(
			self.service.provider,
			paasmaker.util.plugin.MODE.SERVICE_DELETE,
			self.service.parameters,
			self.logger
		)

		service_plugin.remove(self.service.name, self.service.credentials, self._service_success, self._service_failure)

	def _service_success(self, message):
		self.logger.info(message)
		self.session.refresh(self.service)

		self.session.delete(self.service)
		self.session.commit()

		# And signal completion.
		self.success({}, "Successfully deleted service %s" % self.service.name)

	def _service_failure(self, message):
		# Record the new state.
		self.session.refresh(self.service)
		self.service.state = constants.SERVICE.ERROR
		self.session.add(self.service)
		self.session.commit()

		# Signal failure.
		self.failed(message)

class ApplicationDeleteJobTest(tornado.testing.AsyncTestCase, TestHelpers):
	def setUp(self):
		super(ApplicationDeleteJobTest, self).setUp()
		self.configuration = paasmaker.common.configuration.ConfigurationStub(0, ['pacemaker'], io_loop=self.io_loop)
		self.configuration.set_node_uuid(str(uuid.uuid4()))
		# Fire up the job manager.
		self.configuration.startup_job_manager(self.stop)
		self.wait()

		self.configuration.plugins.register(
			'paasmaker.service.parameters',
			'paasmaker.pacemaker.service.parameters.ParametersService',
			{},
			'Parameters Service'
		)

	def tearDown(self):
		self.configuration.cleanup()
		super(ApplicationDeleteJobTest, self).tearDown()

	def on_job_status(self, message):
		#print str(message.flatten())
		self.stop(message)

	def test_simple(self):
		session = self.configuration.get_database_session()

		workspace = paasmaker.model.Workspace()
		workspace.name = 'Test'
		workspace.stub = 'test'

		application = paasmaker.model.Application()
		application.workspace = workspace
		application.name = 'foo.com'

		service = paasmaker.model.Service()
		service.application = application
		service.name = 'test1'
		service.provider = 'paasmaker.service.parameters'
		service.parameters = {'test': 'bar'}
		service.credentials = {'test': 'bar'}
		service.state = paasmaker.common.core.constants.SERVICE.AVAILABLE

		service = paasmaker.model.Service()
		service.application = application
		service.name = 'test2'
		service.provider = 'paasmaker.service.parameters'
		service.parameters = {'test': 'bar'}
		service.credentials = {'test': 'bar'}
		service.state = paasmaker.common.core.constants.SERVICE.AVAILABLE

		service = paasmaker.model.Service()
		service.application = application
		service.name = 'test3'
		service.provider = 'paasmaker.service.parameters'
		service.parameters = {'test': 'bar'}
		service.credentials = {'test': 'bar'}
		service.state = paasmaker.common.core.constants.SERVICE.AVAILABLE

		session.add(application)
		session.commit()

		ApplicationDeleteRootJob.setup_for_application(
			self.configuration,
			application,
			self.stop
		)

		root_job_id = self.wait()

		# Subscribe to updates to the root job.
		pub.subscribe(self.on_job_status, self.configuration.get_job_status_pub_topic(root_job_id))

		# And make it work.
		self.configuration.job_manager.allow_execution(root_job_id, self.stop)
		self.wait()

		result = self.wait()
		while result.state not in (constants.JOB_FINISHED_STATES):
			result = self.wait()

		self.assertEquals(result.state, constants.JOB.SUCCESS, "Application delete job did not succeed")

		session.close()
		session = self.configuration.get_database_session()

		shouldnt_exist = session.query(
			paasmaker.model.Application
		).get(
			application.id
		)

		self.assertIsNone(shouldnt_exist, "Application appears to still exist after being deleted")
<commit_msg>When deleting applications, if the service failure callback was called with an exception, it would fail instead of doing the right thing.<commit_after>
import uuid

import paasmaker
from paasmaker.common.core import constants
from ..base import BaseJob
from paasmaker.util.plugin import MODE
from ...testhelpers import TestHelpers

import logging

import tornado
from pubsub import pub

import colander

# Root job: actual application delete (deletes the objects in the db)
# - service distributor: create jobs under the root for deleting each service
# - service a
# - service b
#
# Things this job doesn't do:
# - clean up packed files and source code. For now, leave this to a cleanup task
# - log files will expire eventually anyway
# - jobs will also expire
# - routing table will have already been dumped (because all instances have stopped before deletion)
# - routing stats: there's not yet a good way to export them

class ApplicationDeleteJobParametersSchema(colander.MappingSchema):
	application_id = colander.SchemaNode(
		colander.Integer(),
		title="Application ID",
		description="ID of the application to be deleted"
	)

class ApplicationDeleteRootJob(BaseJob):
	MODES = {
		MODE.JOB: ApplicationDeleteJobParametersSchema()
	}

	def start_job(self, context):
		session = self.configuration.get_database_session()
		application = session.query(
			paasmaker.model.Application
		).get(
			self.parameters["application_id"]
		)

		if application is None:
			error_msg = "Can't find application of id %d to delete" % self.parameters["application_id"]
			self.logger.error(error_msg)
			self.failed(error_msg)
			return

		session.delete(application)
		session.commit()

		self.success({}, "Deleted application id %d" % self.parameters["application_id"])

	@classmethod
	def setup_for_application(cls, configuration, application, callback):
		tags = [
			'workspace:%d' % application.workspace.id,
			'application:%d' % application.id
		]

		tree = configuration.job_manager.get_specifier()
		tree.set_job(
			'paasmaker.job.application.delete',
			{
				'application_id': application.id
			},
			"Delete application %s" % application.name,
			tags=tags
		)

		for service in application.services:
			servicejob = tree.add_child()
			servicejob.set_job(
				'paasmaker.job.application.delete.service',
				{
					'service_id': service.id
				},
				"Delete service '%s'" % service.name
			)

		def on_tree_added(root_id):
			callback(root_id)

		# Add that entire tree into the job manager.
		configuration.job_manager.add_tree(tree, on_tree_added)

class ApplicationDeleteServiceParametersSchema(colander.MappingSchema):
	service_id = colander.SchemaNode(
		colander.Integer(),
		title="Service ID",
		description="ID of the service to be deleted"
	)

class ApplicationDeleteServiceJob(BaseJob):
	MODES = {
		MODE.JOB: ApplicationDeleteServiceParametersSchema()
	}

	def start_job(self, context):
		self.session = self.configuration.get_database_session()
		self.service = self.session.query(
			paasmaker.model.Service
		).get(
			self.parameters['service_id']
		)

		plugin_exists = self.configuration.plugins.exists(
			self.service.provider,
			paasmaker.util.plugin.MODE.SERVICE_DELETE
		)

		if not plugin_exists:
			self.service.state = constants.SERVICE.ERROR
			self.session.add(self.service)
			self.session.commit()
			self.failed("Plugin with mode SERVICE_DELETE doesn't exist for service %s" %self.service.provider)
			return

		service_plugin = self.configuration.plugins.instantiate(
			self.service.provider,
			paasmaker.util.plugin.MODE.SERVICE_DELETE,
			self.service.parameters,
			self.logger
		)

		service_plugin.remove(self.service.name, self.service.credentials, self._service_success, self._service_failure)

	def _service_success(self, message):
		self.logger.info(message)
		self.session.refresh(self.service)

		self.session.delete(self.service)
		self.session.commit()

		# And signal completion.
		self.success({}, "Successfully deleted service %s" % self.service.name)

	def _service_failure(self, message, exception=None):
		# Record the new state.
		self.session.refresh(self.service)
		self.service.state = constants.SERVICE.ERROR
		self.session.add(self.service)
		self.session.commit()

		if exception:
			self.logger.error("Exception", exc_info=exception)

		# Signal failure.
		self.failed(message)

class ApplicationDeleteJobTest(tornado.testing.AsyncTestCase, TestHelpers):
	def setUp(self):
		super(ApplicationDeleteJobTest, self).setUp()
		self.configuration = paasmaker.common.configuration.ConfigurationStub(0, ['pacemaker'], io_loop=self.io_loop)
		self.configuration.set_node_uuid(str(uuid.uuid4()))
		# Fire up the job manager.
		self.configuration.startup_job_manager(self.stop)
		self.wait()

		self.configuration.plugins.register(
			'paasmaker.service.parameters',
			'paasmaker.pacemaker.service.parameters.ParametersService',
			{},
			'Parameters Service'
		)

	def tearDown(self):
		self.configuration.cleanup()
		super(ApplicationDeleteJobTest, self).tearDown()

	def on_job_status(self, message):
		#print str(message.flatten())
		self.stop(message)

	def test_simple(self):
		session = self.configuration.get_database_session()

		workspace = paasmaker.model.Workspace()
		workspace.name = 'Test'
		workspace.stub = 'test'

		application = paasmaker.model.Application()
		application.workspace = workspace
		application.name = 'foo.com'

		service = paasmaker.model.Service()
		service.application = application
		service.name = 'test1'
		service.provider = 'paasmaker.service.parameters'
		service.parameters = {'test': 'bar'}
		service.credentials = {'test': 'bar'}
		service.state = paasmaker.common.core.constants.SERVICE.AVAILABLE

		service = paasmaker.model.Service()
		service.application = application
		service.name = 'test2'
		service.provider = 'paasmaker.service.parameters'
		service.parameters = {'test': 'bar'}
		service.credentials = {'test': 'bar'}
		service.state = paasmaker.common.core.constants.SERVICE.AVAILABLE

		service = paasmaker.model.Service()
		service.application = application
		service.name = 'test3'
		service.provider = 'paasmaker.service.parameters'
		service.parameters = {'test': 'bar'}
		service.credentials = {'test': 'bar'}
		service.state = paasmaker.common.core.constants.SERVICE.AVAILABLE

		session.add(application)
		session.commit()

		ApplicationDeleteRootJob.setup_for_application(
			self.configuration,
			application,
			self.stop
		)

		root_job_id = self.wait()

		# Subscribe to updates to the root job.
		pub.subscribe(self.on_job_status, self.configuration.get_job_status_pub_topic(root_job_id))

		# And make it work.
		self.configuration.job_manager.allow_execution(root_job_id, self.stop)
		self.wait()

		result = self.wait()
		while result.state not in (constants.JOB_FINISHED_STATES):
			result = self.wait()

		self.assertEquals(result.state, constants.JOB.SUCCESS, "Application delete job did not succeed")

		session.close()
		session = self.configuration.get_database_session()

		shouldnt_exist = session.query(
			paasmaker.model.Application
		).get(
			application.id
		)

		self.assertIsNone(shouldnt_exist, "Application appears to still exist after being deleted")
<|endoftext|>