<commit_before>import numpy as np
from . import _colormixer
from . import _histograms
import threading
from skimage.util import img_as_ubyte

# utilities to make life easier for plugin writers.

try:
    import multiprocessing
    CPU_COUNT = multiprocessing.cpu_count()
except ImportError:
    CPU_COUNT = 2

class GuiLockError(Exception):
    def __init__(self, msg):
        self.msg = msg

    def __str__(self):
        return self.msg

class WindowManager(object):
    ''' A class to keep track of spawned windows,
    and make any needed callback once all the windows,
    are closed.'''
    def __init__(self):
        self._windows = []
        self._callback = None
        self._callback_args = ()
        self._callback_kwargs = {}
        self._gui_lock = False
        self._guikit = ''

    def _check_locked(self):
        if not self._gui_lock:
            raise GuiLockError(\
            'Must first acquire the gui lock before using this image manager')

    def _exec_callback(self):
        if self._callback:
            self._callback(*self._callback_args, **self._callback_kwargs)

    def acquire(self, kit):
        if self._gui_lock:
            raise GuiLockError(\
            'The gui lock can only be acquired by one toolkit per session. \
            The lock is already aquired by %s' % self._guikit)
        else:
            self._gui_lock = True
            self._guikit = str(kit)

    def _release(self, kit):
        # releaseing the lock will lose all references to currently
        # tracked images and the callback.
        # this function is private for reason!
        self._check_locked()
        if str(kit) == self._guikit:
            self._windows = []
            self._callback = None
            self._callback_args = ()
            self._callback_kwargs = {}
            self._gui_lock = False
            self._guikit = ''
        else:
            raise RuntimeError('Only the toolkit that owns the lock may release it')

    def add_window(self, win):
        self._check_locked()
        self._windows.append(win)

    def remove_window(self, win):
        self._check_locked()
        try:
            self._windows.remove(win)
        except ValueError:
            print 'Unable to find referenced window in tracked windows.'
            print 'Ignoring...'
        else:
            if len(self._windows) == 0:
                self._exec_callback()

    def register_callback(self, cb, *cbargs, **cbkwargs):
        self._check_locked()
        self._callback = cb
        self._callback_args = cbargs
        self._callback_kwargs = cbkwargs

    def has_windows(self):
        if len(self._windows) > 0:
            return True
        else:
            return False

window_manager = WindowManager()

def prepare_for_display(npy_img):
    '''Convert a 2D or 3D numpy array of any dtype into a
    3D numpy array with dtype uint8. This array will
    be suitable for use in passing to gui toolkits for
    image display purposes.

    Parameters
    ----------
    npy_img : ndarray, 2D or 3D
        The image to convert for display

    Returns
    -------
    out : ndarray, 3D dtype=np.uint8
        The converted image. This is guaranteed to be a contiguous array.

    Notes
    -----
    If the input image is floating point, it is assumed that the data
    is in the range of 0.0 - 1.0. No check is made to assert this
    condition. The image is then scaled to be in the range 0 - 255
    and then cast to np.uint8

    For all other dtypes, the array is simply cast to np.uint8

    If a 2D array is passed, the single channel is replicated
    to the 2nd and 3rd channels.

    If the array contains an alpha channel, this channel is
    ignored.

    '''
    if npy_img.ndim < 2:
        raise ValueError('Image must be 2D or 3D array')

    height = npy_img.shape[0]
    width = npy_img.shape[1]

    out = np.empty((height, width, 3), dtype=np.uint8)
    npy_img = img_as_ubyte(npy_img)

    if npy_img.ndim == 2 or \
       (npy_img.ndim == 3 and npy_img.shape[2] == 1):
        npy_plane = npy_img.reshape((height, width))
        out[:,:,0] = npy_plane
        out[:,:,1] = npy_plane
        out[:,:,2] = npy_plane

    elif npy_img.ndim == 3:
        if npy_img.shape[2] == 3 or npy_img.shape[2] == 4:
            out[:,:,:3] = npy_img[:,:,:3]
        else:
            raise ValueError('Image must have 1, 3, or 4 channels')

    else:
        raise ValueError('Image must have 2 or 3 dimensions')

    return out

def histograms(img, nbins):
    '''Calculate the channel histograms of the current image.

    Parameters
    ----------
    img : ndarray, ndim=3, dtype=np.uint8
    nbins : int
        The number of bins.

    Returns
    -------
    out : (rcounts, gcounts, bcounts, vcounts)
        The binned histograms of the RGB channels and intensity values.

    This is a NAIVE histogram routine, meant primarily for fast display.

    '''

    return _histograms.histograms(img, nbins)

class ImgThread(threading.Thread):
    def __init__(self, func, *args):
        super(ImgThread, self).__init__()
        self.func = func
        self.args = args

    def run(self):
        self.func(*self.args)

class ThreadDispatch(object):
    def __init__(self, img, stateimg, func, *args):

        width = img.shape[1]
        height = img.shape[0]
        self.cores = CPU_COUNT
        self.threads = []
        self.chunks = []

        if self.cores == 1:
            self.chunks.append((img, stateimg))

        elif self.cores >= 4:
            self.chunks.append((img[:(height/4), :, :],
                                stateimg[:(height/4), :, :]))
            self.chunks.append((img[(height/4):(height/2), :, :],
                                stateimg[(height/4):(height/2), :, :]))
            self.chunks.append((img[(height/2):(3*height/4), :, :],
                                stateimg[(height/2):(3*height/4), :, :]))
            self.chunks.append((img[(3*height/4):, :, :],
                                stateimg[(3*height/4):, :, :]))

        # if they dont have 1, or 4 or more, 2 is good.
        else:
            self.chunks.append((img[:(height/2), :, :],
                                stateimg[:(height/2), :, :]))
            self.chunks.append((img[(height/2):, :, :],
                               stateimg[(height/2):, :, :]))

        for i in range(self.cores):
            self.threads.append(ImgThread(func, self.chunks[i][0],
                                          self.chunks[i][1], *args))

    def run(self):
        for t in self.threads:
            t.start()
        for t in self.threads:
            t.join()

class ColorMixer(object):
    ''' a class to manage mixing colors in an image.
    The input array must be an RGB uint8 image.

    The mixer maintains an original copy of the image,
    and uses this copy to query the pixel data for operations.
    It also makes a copy for sharing state across operations.
    That is, if you add to a channel, and multiply to same channel,
    the two operations are carried separately and the results
    averaged together.

    it modifies your array in place. This ensures that if you
    bust over a threshold, you can always come back down.

    The passed values to a function are always considered
    absolute. Thus to threshold a channel completely you
    can do mixer.add(RED, 255). Or to double the intensity
    of the blue channel: mixer.multiply(BLUE, 2.)

    To reverse these operations, respectively:
    mixer.add(RED, 0), mixer.multiply(BLUE, 1.)

    The majority of the backend is implemented in Cython,
    so it should be quite quick.
    '''

    RED = 0
    GREEN = 1
    BLUE = 2

    valid_channels = [RED, GREEN, BLUE]

    def __init__(self, img):
        if type(img) != np.ndarray:
            raise ValueError('Image must be a numpy array')
        if img.dtype != np.uint8:
            raise ValueError('Image must have dtype uint8')
        if img.ndim != 3 or img.shape[2] != 3:
            raise ValueError('Image must be 3 channel MxNx3')

        self.img = img
        self.origimg = img.copy()
        self.stateimg = img.copy()

    def get_stateimage(self):
        return self.stateimg

    def commit_changes(self):
        self.stateimg[:] = self.img[:]

    def revert(self):
        self.stateimg[:] = self.origimg[:]
        self.img[:] = self.stateimg[:]

    def set_to_stateimg(self):
        self.img[:] = self.stateimg[:]

    def add(self, channel, ammount):
        '''Add the specified ammount to the specified channel.

        Parameters
        ----------
        channel : flag
            the color channel to operate on
            RED, GREED, or BLUE
        ammount : integer
            the ammount of color to add to the channel,
            can be positive or negative.

        '''
        assert channel in self.valid_channels
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.add, channel, ammount)
        pool.run()

    def multiply(self, channel, ammount):
        '''Mutliply the indicated channel by the specified value.

         Parameters
        ----------
        channel : flag
            the color channel to operate on
            RED, GREED, or BLUE
        ammount : integer
            the ammount of color to add to the channel,
            can be positive or negative.

        '''
        assert channel in self.valid_channels
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.multiply, channel, ammount)
        pool.run()

    def brightness(self, factor, offset):
        '''Adjust the brightness off an image with an offset and factor.

        Parameters
        ----------
        offset : integer
            The ammount to add to each channel.
        factor : float
            The factor to multiply each channel by.

        result = clip((pixel + offset)*factor)

        '''
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.brightness, factor, offset)
        pool.run()

    def sigmoid_gamma(self, alpha, beta):
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.sigmoid_gamma, alpha, beta)
        pool.run()

    def gamma(self, gamma):
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.gamma, gamma)
        pool.run()

    def hsv_add(self, h_amt, s_amt, v_amt):
        '''Adjust the H, S, V channels of an image by a constant ammount.
        This is similar to the add() mixer function, but operates over the
        entire image at once. Thus all three additive values, H, S, V, must
        be supplied simultaneously.

        Parameters
        ----------
        h_amt : float
            The ammount to add to the hue (-180..180)
        s_amt : float
            The ammount to add to the saturation (-1..1)
        v_amt : float
            The ammount to add to the value (-1..1)

        '''
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.hsv_add, h_amt, s_amt, v_amt)
        pool.run()

    def hsv_multiply(self, h_amt, s_amt, v_amt):
        '''Adjust the H, S, V channels of an image by a constant ammount.
        This is similar to the add() mixer function, but operates over the
        entire image at once. Thus all three additive values, H, S, V, must
        be supplied simultaneously.

        Note that since hue is in degrees, it makes no sense to multiply
        that channel, thus an add operation is performed on the hue. And the
        values given for h_amt, should be the same as for hsv_add

        Parameters
        ----------
        h_amt : float
            The ammount to to add to the hue (-180..180)
        s_amt : float
            The ammount to multiply to the saturation (0..1)
        v_amt : float
            The ammount to multiply to the value (0..1)

        '''
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.hsv_multiply, h_amt, s_amt, v_amt)
        pool.run()

    def rgb_2_hsv_pixel(self, R, G, B):
        '''Convert an RGB value to HSV

        Parameters
        ----------
        R : int
            Red value
        G : int
            Green value
        B : int
            Blue value

        Returns
        -------
        out : (H, S, V) Floats
            The HSV values

        '''
        H, S, V = _colormixer.py_rgb_2_hsv(R, G, B)
        return (H, S, V)

    def hsv_2_rgb_pixel(self, H, S, V):
        '''Convert an HSV value to RGB

        Parameters
        ----------
        H : float
            Hue value
        S : float
            Saturation value
        V : float
            Intensity value

        Returns
        -------
        out : (R, G, B) ints
            The RGB values

        '''
        R, G, B = _colormixer.py_hsv_2_rgb(H, S, V)
        return (R, G, B)

<commit_msg>BUG: Dispatch threads to process entire image.<commit_after>import numpy as np
from . import _colormixer
from . import _histograms
import threading
from skimage.util import img_as_ubyte

# utilities to make life easier for plugin writers.

try:
    import multiprocessing
    CPU_COUNT = multiprocessing.cpu_count()
except ImportError:
    CPU_COUNT = 2

class GuiLockError(Exception):
    def __init__(self, msg):
        self.msg = msg

    def __str__(self):
        return self.msg

class WindowManager(object):
    ''' A class to keep track of spawned windows,
    and make any needed callback once all the windows,
    are closed.'''
    def __init__(self):
        self._windows = []
        self._callback = None
        self._callback_args = ()
        self._callback_kwargs = {}
        self._gui_lock = False
        self._guikit = ''

    def _check_locked(self):
        if not self._gui_lock:
            raise GuiLockError(\
            'Must first acquire the gui lock before using this image manager')

    def _exec_callback(self):
        if self._callback:
            self._callback(*self._callback_args, **self._callback_kwargs)

    def acquire(self, kit):
        if self._gui_lock:
            raise GuiLockError(\
            'The gui lock can only be acquired by one toolkit per session. \
            The lock is already aquired by %s' % self._guikit)
        else:
            self._gui_lock = True
            self._guikit = str(kit)

    def _release(self, kit):
        # releaseing the lock will lose all references to currently
        # tracked images and the callback.
        # this function is private for reason!
        self._check_locked()
        if str(kit) == self._guikit:
            self._windows = []
            self._callback = None
            self._callback_args = ()
            self._callback_kwargs = {}
            self._gui_lock = False
            self._guikit = ''
        else:
            raise RuntimeError('Only the toolkit that owns the lock may release it')

    def add_window(self, win):
        self._check_locked()
        self._windows.append(win)

    def remove_window(self, win):
        self._check_locked()
        try:
            self._windows.remove(win)
        except ValueError:
            print 'Unable to find referenced window in tracked windows.'
            print 'Ignoring...'
        else:
            if len(self._windows) == 0:
                self._exec_callback()

    def register_callback(self, cb, *cbargs, **cbkwargs):
        self._check_locked()
        self._callback = cb
        self._callback_args = cbargs
        self._callback_kwargs = cbkwargs

    def has_windows(self):
        if len(self._windows) > 0:
            return True
        else:
            return False

window_manager = WindowManager()

def prepare_for_display(npy_img):
    '''Convert a 2D or 3D numpy array of any dtype into a
    3D numpy array with dtype uint8. This array will
    be suitable for use in passing to gui toolkits for
    image display purposes.

    Parameters
    ----------
    npy_img : ndarray, 2D or 3D
        The image to convert for display

    Returns
    -------
    out : ndarray, 3D dtype=np.uint8
        The converted image. This is guaranteed to be a contiguous array.

    Notes
    -----
    If the input image is floating point, it is assumed that the data
    is in the range of 0.0 - 1.0. No check is made to assert this
    condition. The image is then scaled to be in the range 0 - 255
    and then cast to np.uint8

    For all other dtypes, the array is simply cast to np.uint8

    If a 2D array is passed, the single channel is replicated
    to the 2nd and 3rd channels.

    If the array contains an alpha channel, this channel is
    ignored.

    '''
    if npy_img.ndim < 2:
        raise ValueError('Image must be 2D or 3D array')

    height = npy_img.shape[0]
    width = npy_img.shape[1]

    out = np.empty((height, width, 3), dtype=np.uint8)
    npy_img = img_as_ubyte(npy_img)

    if npy_img.ndim == 2 or \
       (npy_img.ndim == 3 and npy_img.shape[2] == 1):
        npy_plane = npy_img.reshape((height, width))
        out[:,:,0] = npy_plane
        out[:,:,1] = npy_plane
        out[:,:,2] = npy_plane

    elif npy_img.ndim == 3:
        if npy_img.shape[2] == 3 or npy_img.shape[2] == 4:
            out[:,:,:3] = npy_img[:,:,:3]
        else:
            raise ValueError('Image must have 1, 3, or 4 channels')

    else:
        raise ValueError('Image must have 2 or 3 dimensions')

    return out

def histograms(img, nbins):
    '''Calculate the channel histograms of the current image.

    Parameters
    ----------
    img : ndarray, ndim=3, dtype=np.uint8
    nbins : int
        The number of bins.

    Returns
    -------
    out : (rcounts, gcounts, bcounts, vcounts)
        The binned histograms of the RGB channels and intensity values.

    This is a NAIVE histogram routine, meant primarily for fast display.

    '''

    return _histograms.histograms(img, nbins)

class ImgThread(threading.Thread):
    def __init__(self, func, *args):
        super(ImgThread, self).__init__()
        self.func = func
        self.args = args

    def run(self):
        self.func(*self.args)

class ThreadDispatch(object):
    def __init__(self, img, stateimg, func, *args):

        width = img.shape[1]
        height = img.shape[0]
        self.cores = CPU_COUNT
        self.threads = []
        self.chunks = []

        if self.cores == 1:
            self.chunks.append((img, stateimg))

        elif self.cores >= 4:
            self.chunks.append((img[:(height/4), :, :],
                                stateimg[:(height/4), :, :]))
            self.chunks.append((img[(height/4):(height/2), :, :],
                                stateimg[(height/4):(height/2), :, :]))
            self.chunks.append((img[(height/2):(3*height/4), :, :],
                                stateimg[(height/2):(3*height/4), :, :]))
            self.chunks.append((img[(3*height/4):, :, :],
                                stateimg[(3*height/4):, :, :]))

        # if they dont have 1, or 4 or more, 2 is good.
        else:
            self.chunks.append((img[:(height/2), :, :],
                                stateimg[:(height/2), :, :]))
            self.chunks.append((img[(height/2):, :, :],
                               stateimg[(height/2):, :, :]))

        for i in range(len(self.chunks)):
            self.threads.append(ImgThread(func, self.chunks[i][0],
                                          self.chunks[i][1], *args))

    def run(self):
        for t in self.threads:
            t.start()
        for t in self.threads:
            t.join()

class ColorMixer(object):
    ''' a class to manage mixing colors in an image.
    The input array must be an RGB uint8 image.

    The mixer maintains an original copy of the image,
    and uses this copy to query the pixel data for operations.
    It also makes a copy for sharing state across operations.
    That is, if you add to a channel, and multiply to same channel,
    the two operations are carried separately and the results
    averaged together.

    it modifies your array in place. This ensures that if you
    bust over a threshold, you can always come back down.

    The passed values to a function are always considered
    absolute. Thus to threshold a channel completely you
    can do mixer.add(RED, 255). Or to double the intensity
    of the blue channel: mixer.multiply(BLUE, 2.)

    To reverse these operations, respectively:
    mixer.add(RED, 0), mixer.multiply(BLUE, 1.)

    The majority of the backend is implemented in Cython,
    so it should be quite quick.
    '''

    RED = 0
    GREEN = 1
    BLUE = 2

    valid_channels = [RED, GREEN, BLUE]

    def __init__(self, img):
        if type(img) != np.ndarray:
            raise ValueError('Image must be a numpy array')
        if img.dtype != np.uint8:
            raise ValueError('Image must have dtype uint8')
        if img.ndim != 3 or img.shape[2] != 3:
            raise ValueError('Image must be 3 channel MxNx3')

        self.img = img
        self.origimg = img.copy()
        self.stateimg = img.copy()

    def get_stateimage(self):
        return self.stateimg

    def commit_changes(self):
        self.stateimg[:] = self.img[:]

    def revert(self):
        self.stateimg[:] = self.origimg[:]
        self.img[:] = self.stateimg[:]

    def set_to_stateimg(self):
        self.img[:] = self.stateimg[:]

    def add(self, channel, ammount):
        '''Add the specified ammount to the specified channel.

        Parameters
        ----------
        channel : flag
            the color channel to operate on
            RED, GREED, or BLUE
        ammount : integer
            the ammount of color to add to the channel,
            can be positive or negative.

        '''
        assert channel in self.valid_channels
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.add, channel, ammount)
        pool.run()

    def multiply(self, channel, ammount):
        '''Mutliply the indicated channel by the specified value.

         Parameters
        ----------
        channel : flag
            the color channel to operate on
            RED, GREED, or BLUE
        ammount : integer
            the ammount of color to add to the channel,
            can be positive or negative.

        '''
        assert channel in self.valid_channels
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.multiply, channel, ammount)
        pool.run()

    def brightness(self, factor, offset):
        '''Adjust the brightness off an image with an offset and factor.

        Parameters
        ----------
        offset : integer
            The ammount to add to each channel.
        factor : float
            The factor to multiply each channel by.

        result = clip((pixel + offset)*factor)

        '''
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.brightness, factor, offset)
        pool.run()

    def sigmoid_gamma(self, alpha, beta):
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.sigmoid_gamma, alpha, beta)
        pool.run()

    def gamma(self, gamma):
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.gamma, gamma)
        pool.run()

    def hsv_add(self, h_amt, s_amt, v_amt):
        '''Adjust the H, S, V channels of an image by a constant ammount.
        This is similar to the add() mixer function, but operates over the
        entire image at once. Thus all three additive values, H, S, V, must
        be supplied simultaneously.

        Parameters
        ----------
        h_amt : float
            The ammount to add to the hue (-180..180)
        s_amt : float
            The ammount to add to the saturation (-1..1)
        v_amt : float
            The ammount to add to the value (-1..1)

        '''
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.hsv_add, h_amt, s_amt, v_amt)
        pool.run()

    def hsv_multiply(self, h_amt, s_amt, v_amt):
        '''Adjust the H, S, V channels of an image by a constant ammount.
        This is similar to the add() mixer function, but operates over the
        entire image at once. Thus all three additive values, H, S, V, must
        be supplied simultaneously.

        Note that since hue is in degrees, it makes no sense to multiply
        that channel, thus an add operation is performed on the hue. And the
        values given for h_amt, should be the same as for hsv_add

        Parameters
        ----------
        h_amt : float
            The ammount to to add to the hue (-180..180)
        s_amt : float
            The ammount to multiply to the saturation (0..1)
        v_amt : float
            The ammount to multiply to the value (0..1)

        '''
        pool = ThreadDispatch(self.img, self.stateimg,
                              _colormixer.hsv_multiply, h_amt, s_amt, v_amt)
        pool.run()

    def rgb_2_hsv_pixel(self, R, G, B):
        '''Convert an RGB value to HSV

        Parameters
        ----------
        R : int
            Red value
        G : int
            Green value
        B : int
            Blue value

        Returns
        -------
        out : (H, S, V) Floats
            The HSV values

        '''
        H, S, V = _colormixer.py_rgb_2_hsv(R, G, B)
        return (H, S, V)

    def hsv_2_rgb_pixel(self, H, S, V):
        '''Convert an HSV value to RGB

        Parameters
        ----------
        H : float
            Hue value
        S : float
            Saturation value
        V : float
            Intensity value

        Returns
        -------
        out : (R, G, B) ints
            The RGB values

        '''
        R, G, B = _colormixer.py_hsv_2_rgb(H, S, V)
        return (R, G, B)

<|endoftext|>


<commit_before>#!/usr/bin/env python
#
# Copyright 2016 Robert King, Jacky Liang, Troy Mullins, and Thomas Norby
# Licensed under MIT
# (https://github.com/Mullinst/SER-FP/blob/master/LICENSE)
#
# database_functions.py -- All functions for interacting with the database.

import psycopg2

def connect(database_name="database"):
    """Connect to the PostgreSQL database.  Returns a database connection."""
    try:
        db = psycopg2.connect("dbname={}".format(database_name))
        cursor = db.cursor()
        return db, cursor
    except:
        print("Error connecting to the database: {}".format(database_name))

# ---------------- #
#  User Functions  #
# ---------------- #
def createUser(login_session):
    """ creates a user using login session information """
    try:
        db, cursor = connect()
        name = login_session.get('username')
        email = login_session.get('email')
        picture = login_session.get('picture')
        query = "INSERT INTO Users (name, email, picture) VALUES (%s,%s,%s);"
        param = (name, email, picture,)
        cursor.execute(query, param)
        query = "SELECT id FROM Users WHERE email = %s;"
        param = (email,)
        cursor.execute(query, param)
        user_id = cursor.fetchone()[0]
        query = "INSERT INTO Permissions (user_id) VALUES (%s);"
        param = (user_id,)
        cursor.execute(query, param)
        db.commit()
        db.close()
        return user_id
    except:
        return None

def changeUserType(user_id, userType):
    """ Change userType of given user. """
    try:
        if userType == 'Employee' or userType == 'Store Manager' or userType == 'Admin':
            db, cursor = connect()
            query = "UPDATE Users SET userType = %s WHERE id = %s"
            param = (userType, user_id,)
            cursor.execute(query, param)
            db.commit()
            db.close()
            return userType
        else:
            return None
    except:
        return None

def editUserPermissions(current_user_id, user_id, post=True,
        accept=True, approve_requests=False,
        delete_requests=False, edit_permissions=False):
    """ Edit given users permissions.
    Args:
        current_user_id: User id of the user attempting
            to edit permissions.
        user_id: An integer referencing the user whose
            permissions will be editted.

    Optional Args:
        post: Boolean value referencing posting permission.
        accept: Boolean value referencing permission to accept shifts.
        approve_requests: Boolean value referencing permission
            to approve shift changes.
        delete_requests: Boolean value referencing permission
            to delete users posts.
        edit_permissions: Boolean value referencing Permissions
            to edit users permissions.
    """
    try:
        db, cursor = connect()
        # Check that current user has permission to edit permissions.
        query = ("SELECT edit_permissions FROM Permissions "
                 "WHERE user_id = %s;")
        param = (current_user_id,)
        cursor.execute(query, param)
        user_edit_permissions = cursor.fetchone()[0]
        if user_edit_permissions == True:
            # If the current user has permission to edit permissions
            # edit the permissions of the defined user.
            if current_user_id == user_id:
                # If the current user has permission to edit override defualt.
                edit_permissions = True
            query = ("UPDATE Permissions SET post = %s, accept = %s,"
                     "approve_requests = %s, delete_requests = %s,"
                     "edit_permissions = %s WHERE user_id = %s;")
            param = (post, accept, approve_requests, delete_requests,
                     edit_permissions, int(user_id),)
            print param
            cursor.execute(query, param)
            db.commit()
            db.close()
            return True
    except:
        return None

def deleteUser(user_id):
    """ Deletes user from users table and returns the users information """
    try:
        db, cursor = connect()
        # Delete user permissions
        query = "DELETE FROM Permissions WHERE user_id = %s;"
        param = (user_id,)
        cursor.execute(query, param)
        # Delete user from Users table
        query = "DELETE FROM Users WHERE id = %s RETURNING *;"
        cursor.execute(query, param)
        user = cursor.fetchall()
        db.commit()
        db.close()
        return user
    except:
        return None

def getUserInfo(user_id):
    """ Returns user iformation for specified user """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Users WHERE id = %s;"
        param = (user_id,)
        cursor.execute(query, param)
        user = cursor.fetchall()
        db.close()
        return user
    except:
        return None

def getUserID(email):
    """ Returns user id associated with given email """
    try:
        db, cursor = connect()
        query = "SELECT id FROM Users WHERE email = %s;"
        param = (email,)
        cursor.execute(query, param)
        user_id = cursor.fetchone()[0]
        db.close()
        return user_id
    except:
        return None

def getUserType(user_id):
    """ Returns userType for given user. """
    try:
        db, cursor = connect()
        query = "SELECT userType FROM Users WHERE id = %s;"
        param = (user_id,)
        cursor.execute(query, param)
        userType = cursor.fetchone()[0]
        db.close()
        return userType
    except:
        return None

def getApplicants():
    """ Returns the id and name pairs of all applicants. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM applicants;"
        cursor.execute(query)
        applicants = cursor.fetchall()
        db.close()
        return applicants
    except:
        return None

def getUsers():
    """ Returns id and name pairs for all users. """
    try:
        db, cursor = connect()
        query = "SELECT id, name FROM Users;"
        cursor.execute(query)
        users = cursor.fetchall()
        db.close()
        return users
    except:
        return None

def getStoreManager(storeID):
    """ Returns the name of the store manager for a given store. """
    try:
        db, cursor = connect()
        query = "SELECT storeManagerID FROM Stores WHERE storeID = %s;"
        params = (storeID,)
        cursor.execute(query, params)
        storeManagerID = cursor.fetchone()[0]
        query = "SELECT name FROM Users WHERE id = %s;"
        params = (storeManagerID,)
        cursor.execute(query, params)
        name = cursor.fetchone()[0]
        db.close()
        return name
    except:
        return None

# ----------------- #
#  Shift Functions  #
# ----------------- #
def createShift(user_id, shift_number, date, is_urgent=False):
    """ Creates shift cover request """
    try:
        db, cursor = connect()
        query = "SELECT storeID FROM Users WHERE id = %s;"
        param = (user_id,)
        cursor.execute(query, param)
        storeID = cursor.fetchone()[0]
        query = "INSERT INTO Shifts (requestor_ID, shift_number, shift_day, isUrgent, storeID) VALUES (%s,%s,%s,%s,%s);"
        param = (user_id, int(shift_number), str(date), is_urgent, storeID,)
        cursor.execute(query, param)
        db.commit()
        db.close()
        return True
    except:
        return None

def editShift(shift_id, shift_number, date, is_urgent=False):
    """ Edit given shift """
    try:
        db, cursor = connect()
        query = "UPDATE Shifts SET shift_number = %s, shift_day = %s, isUrgent = %s WHERE shiftID = %s;"
        param = (int(shift_number), str(date), is_urgent, shift_id,)
        cursor.execute(query, param)
        db.commit()
        db.close()
        return True
    except:
        return None

def deleteShift(user_id, shift_id):
    """ Deletes given shift """
    try:
        db, cursor = connect()
        # Get requestor ID
        query = "SELECT requestor_ID FROM Shifts WHERE shiftID = %s"
        param = (shift_id,)
        cursor.execute(query, param)
        ownerID = cursor.fetchone()[0]
        # Get users delete permission
        query = "SELECT delete_requests FROM Permissions WHERE user_id = %s"
        param = (user_id,)
        cursor.execute(query, param)
        userDeletePerm = cursor.fetchone()[0]
        if user_id == ownerID or userDeletePerm == True:
            query = "DELETE FROM Shifts WHERE shiftID = %s;"
            param = (shift_id,)
            cursor.execute(query, param)
            db.commit()
            db.close()
            return True
    except:
        return None

def acceptShift(user_id, shift_id):
    """ Updates shift to reflect acceptors information. """
    try:
        db, cursor = connect()
        query = "UPDATE Shifts SET acceptor_ID = %s, status = 'Pending' WHERE shiftID = %s;"
        param = (user_id,shift_id,)
        cursor.execute(query, param)
        db.commit()
        db.close()
        return True
    except:
        return None

def getRequestedShiftChanges(user_id):
    """ Returns all shift change requests created by the user. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Shifts WHERE requestor_id = %s ORDER BY shift_day;"
        params = (user_id,)
        cursor.execute(query, params)
        shifts = cursor.fetchall()
        db.close()
        return shifts
    except:
        return None

def getPendingShiftChangesByStore(store_id):
    """ Returns all pending shift changes for a given store. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Shifts WHERE storeID = %s AND status = 'Pending' ORDER BY shift_day;"
        params = (store_id,)
        cursor.execute(query, params)
        shifts = cursor.fetchall()
        db.close()
        return shifts
    except:
        return None

def getPendingShiftChangesByUser(user_id):
    """ Returns all pending shift changes a given user is involved in. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Shifts WHERE (requestor_id = %s or acceptor_ID = %s) AND status = 'Pending' ORDER BY shift_day;"
        params = (user_id, user_id,)
        cursor.execute(query, params)
        shifts = cursor.fetchall()
        db.close()
        return shifts
    except:
        return None

def getCurrentStoreNonUrgentShifts(user_id, store_id):
    """ Returns all the shifts for a given store. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Shifts WHERE requestor_ID != %s AND storeID = %s AND status = 'Active' AND isUrgent = false ORDER BY shift_day;"
        params = (user_id, store_id,)
        cursor.execute(query, params)
        shifts = cursor.fetchall()
        db.close()
        return shifts
    except:
        return None

def getCurrentStoreUrgentShifts(user_id, store_id):
    """ Returns all the shifts for a given store. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Shifts WHERE requestor_ID != %s AND  storeID = %s AND status = 'Active' AND isUrgent = true ORDER BY shift_day;"
        params = (user_id, store_id,)
        cursor.execute(query, params)
        shifts = cursor.fetchall()
        db.close()
        return shifts
    except:
        return None
<commit_msg>fix: minor changes.<commit_after>#!/usr/bin/env python
#
# Copyright 2016 Robert King, Jacky Liang, Troy Mullins, and Thomas Norby
# Licensed under MIT
# (https://github.com/Mullinst/SER-FP/blob/master/LICENSE)
#
# database_functions.py -- All functions for interacting with the database.

import psycopg2

def connect(database_name="database"):
    """Connect to the PostgreSQL database.  Returns a database connection."""
    try:
        db = psycopg2.connect("dbname={}".format(database_name))
        cursor = db.cursor()
        return db, cursor
    except:
        print("Error connecting to the database: {}".format(database_name))

# ---------------- #
#  User Functions  #
# ---------------- #
def createUser(login_session):
    """ creates a user using login session information """
    try:
        db, cursor = connect()
        name = login_session.get('username')
        email = login_session.get('email')
        picture = login_session.get('picture')
        query = "INSERT INTO Users (name, email, picture) VALUES (%s,%s,%s);"
        param = (name, email, picture,)
        cursor.execute(query, param)
        query = "SELECT id FROM Users WHERE email = %s;"
        param = (email,)
        cursor.execute(query, param)
        user_id = cursor.fetchone()[0]
        query = "INSERT INTO Permissions (user_id) VALUES (%s);"
        param = (user_id,)
        cursor.execute(query, param)
        db.commit()
        db.close()
        return user_id
    except:
        return None

def changeUserType(user_id, userType):
    """ Change userType of given user. """
    try:
        if userType == 'Employee' or userType == 'Store Manager' or userType == 'Admin':
            db, cursor = connect()
            query = "UPDATE Users SET userType = %s WHERE id = %s"
            param = (userType, user_id,)
            cursor.execute(query, param)
            query = "SELECT userType FROM Users WHERE id = %s"
            param = (user_id,)
            cursor.execute(query, param)
            selected_type = cursor.fetchone()[0]
            if selected_type == 'Admin':
                # By default, all admins have permission to edit permissions.
                query = "UPDATE Permissions SET edit_permissions = true WHERE id = %s"
                param = (user_id,)
                cursor.execute(query, param)
            db.commit()
            db.close()
            return selected_type
        else:
            return None
    except:
        return None

def editUserPermissions(current_user_id, user_id, post=True,
        accept=True, approve_requests=False,
        delete_requests=False, edit_permissions=False):
    """ Edit given users permissions.
    Args:
        current_user_id: User id of the user attempting
            to edit permissions.
        user_id: An integer referencing the user whose
            permissions will be editted.

    Optional Args:
        post: Boolean value referencing posting permission.
        accept: Boolean value referencing permission to accept shifts.
        approve_requests: Boolean value referencing permission
            to approve shift changes.
        delete_requests: Boolean value referencing permission
            to delete users posts.
        edit_permissions: Boolean value referencing Permissions
            to edit users permissions.
    """
    try:
        db, cursor = connect()
        # Check that current user has permission to edit permissions.
        query = ("SELECT edit_permissions FROM Permissions "
                 "WHERE user_id = %s;")
        param = (current_user_id,)
        cursor.execute(query, param)
        user_edit_permissions = cursor.fetchone()[0]
        if user_edit_permissions == True:
            # If the current user has permission to edit permissions
            # edit the permissions of the defined user.
            if current_user_id == user_id:
                # If the current user has permission to edit override defualt.
                edit_permissions = True
            query = ("UPDATE Permissions SET post = %s, accept = %s,"
                     "approve_requests = %s, delete_requests = %s,"
                     "edit_permissions = %s WHERE user_id = %s;")
            param = (post, accept, approve_requests, delete_requests,
                     edit_permissions, int(user_id),)
            print param
            cursor.execute(query, param)
            db.commit()
            db.close()
            return True
    except:
        return None

def deleteUser(user_id):
    """ Deletes user from users table and returns the users information """
    try:
        db, cursor = connect()
        # Delete user permissions
        query = "DELETE FROM Permissions WHERE user_id = %s;"
        param = (user_id,)
        cursor.execute(query, param)
        # Delete user from Users table
        query = "DELETE FROM Users WHERE id = %s RETURNING *;"
        cursor.execute(query, param)
        user = cursor.fetchall()
        db.commit()
        db.close()
        return user
    except:
        return None

def getUserInfo(user_id):
    """ Returns user iformation for specified user """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Users WHERE id = %s;"
        param = (user_id,)
        cursor.execute(query, param)
        user = cursor.fetchall()
        db.close()
        return user
    except:
        return None

def getUserID(email):
    """ Returns user id associated with given email """
    try:
        db, cursor = connect()
        query = "SELECT id FROM Users WHERE email = %s;"
        param = (email,)
        cursor.execute(query, param)
        user_id = cursor.fetchone()[0]
        db.close()
        return user_id
    except:
        return None

def getUserType(user_id):
    """ Returns userType for given user. """
    try:
        db, cursor = connect()
        query = "SELECT userType FROM Users WHERE id = %s;"
        param = (user_id,)
        cursor.execute(query, param)
        userType = cursor.fetchone()[0]
        db.close()
        return userType
    except:
        return None

def getApplicants():
    """ Returns the id and name pairs of all applicants. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM applicants;"
        cursor.execute(query)
        applicants = cursor.fetchall()
        db.close()
        return applicants
    except:
        return None

def getUsers():
    """ Returns id and name pairs for all users. """
    try:
        db, cursor = connect()
        query = "SELECT id, name FROM Users;"
        cursor.execute(query)
        users = cursor.fetchall()
        db.close()
        return users
    except:
        return None

def getStoreManager(storeID):
    """ Returns the name of the store manager for a given store. """
    try:
        db, cursor = connect()
        query = "SELECT storeManagerID FROM Stores WHERE storeID = %s;"
        params = (storeID,)
        cursor.execute(query, params)
        storeManagerID = cursor.fetchone()[0]
        query = "SELECT name FROM Users WHERE id = %s;"
        params = (storeManagerID,)
        cursor.execute(query, params)
        name = cursor.fetchone()[0]
        db.close()
        return name
    except:
        return None

# ----------------- #
#  Shift Functions  #
# ----------------- #
def createShift(user_id, shift_number, date, is_urgent=False):
    """ Creates shift cover request """
    try:
        db, cursor = connect()
        query = "SELECT storeID FROM Users WHERE id = %s;"
        param = (user_id,)
        cursor.execute(query, param)
        storeID = cursor.fetchone()[0]
        query = "INSERT INTO Shifts (requestor_ID, shift_number, shift_day, isUrgent, storeID) VALUES (%s,%s,%s,%s,%s);"
        param = (user_id, int(shift_number), str(date), is_urgent, storeID,)
        cursor.execute(query, param)
        db.commit()
        db.close()
        return True
    except:
        return None

def editShift(shift_id, shift_number, date, is_urgent=False):
    """ Edit given shift """
    try:
        db, cursor = connect()
        query = "UPDATE Shifts SET shift_number = %s, shift_day = %s, isUrgent = %s WHERE shiftID = %s;"
        param = (int(shift_number), str(date), is_urgent, shift_id,)
        cursor.execute(query, param)
        db.commit()
        db.close()
        return True
    except:
        return None

def deleteShift(user_id, shift_id):
    """ Deletes given shift """
    try:
        db, cursor = connect()
        # Get requestor ID
        query = "SELECT requestor_ID FROM Shifts WHERE shiftID = %s"
        param = (shift_id,)
        cursor.execute(query, param)
        ownerID = cursor.fetchone()[0]
        # Get users delete permission
        query = "SELECT delete_requests FROM Permissions WHERE user_id = %s"
        param = (user_id,)
        cursor.execute(query, param)
        userDeletePerm = cursor.fetchone()[0]
        if user_id == ownerID or userDeletePerm == True:
            query = "DELETE FROM Shifts WHERE shiftID = %s;"
            param = (shift_id,)
            cursor.execute(query, param)
            db.commit()
            db.close()
            return True
    except:
        return None

def acceptShift(user_id, shift_id):
    """ Updates shift to reflect acceptors information. """
    try:
        db, cursor = connect()
        query = "UPDATE Shifts SET acceptor_ID = %s, status = 'Pending' WHERE shiftID = %s;"
        param = (user_id,shift_id,)
        cursor.execute(query, param)
        db.commit()
        db.close()
        return True
    except:
        return None

def getRequestedShiftChanges(user_id):
    """ Returns all shift change requests created by the user. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Shifts WHERE requestor_id = %s ORDER BY shift_day;"
        params = (user_id,)
        cursor.execute(query, params)
        shifts = cursor.fetchall()
        db.close()
        return shifts
    except:
        return None

def getPendingShiftChangesByStore(store_id):
    """ Returns all pending shift changes for a given store. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Shifts WHERE storeID = %s AND status = 'Pending' ORDER BY shift_day;"
        params = (store_id,)
        cursor.execute(query, params)
        shifts = cursor.fetchall()
        db.close()
        return shifts
    except:
        return None

def getPendingShiftChangesByUser(user_id):
    """ Returns all pending shift changes a given user is involved in. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Shifts WHERE (requestor_id = %s or acceptor_ID = %s) AND status = 'Pending' ORDER BY shift_day;"
        params = (user_id, user_id,)
        cursor.execute(query, params)
        shifts = cursor.fetchall()
        db.close()
        return shifts
    except:
        return None

def getCurrentStoreNonUrgentShifts(user_id, store_id):
    """ Returns all the shifts for a given store. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Shifts WHERE requestor_ID != %s AND storeID = %s AND status = 'Active' AND isUrgent = false ORDER BY shift_day;"
        params = (user_id, store_id,)
        cursor.execute(query, params)
        shifts = cursor.fetchall()
        db.close()
        return shifts
    except:
        return None

def getCurrentStoreUrgentShifts(user_id, store_id):
    """ Returns all the shifts for a given store. """
    try:
        db, cursor = connect()
        query = "SELECT * FROM Shifts WHERE requestor_ID != %s AND  storeID = %s AND status = 'Active' AND isUrgent = true ORDER BY shift_day;"
        params = (user_id, store_id,)
        cursor.execute(query, params)
        shifts = cursor.fetchall()
        db.close()
        return shifts
    except:
        return None
<|endoftext|>


<commit_before>import boto
from fabric.colors import red
from fabric.api import task
from time import sleep

import logging
logging.basicConfig()
log = logging.getLogger(__name__)

def create_buckets(shortname):

	conn = boto.connect_s3()

	buckets = (shortname, shortname + '-staging', shortname + '-dev')
	tagset = boto.s3.tagging.TagSet()
	tagset.add_tag("project",shortname)

	tags = boto.s3.tagging.Tags()
	tags.add_tag_set(tagset)

	for bucket in buckets:
		conn.create_bucket(bucket)
		bucket = conn.get_bucket(bucket)
		bucket.set_tags(tags)
		bucket.make_public(True)

policy = """{{
"Statement": [
        {{
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "arn:aws:s3:::*"
        }},
        {{
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::{0}-dev",
                "arn:aws:s3:::{0}-dev/*",
                "arn:aws:s3:::{0}-staging",
                "arn:aws:s3:::{0}-staging/*",
                "arn:aws:s3:::{0}-prod",
                "arn:aws:s3:::{0}-prod/*"

            ]:
        }}
    ]
}}"""

def create_user(shortname=None):

	conn = boto.connect_iam()
	response = conn.create_group(shortname)
	response = conn.put_group_policy(shortname, shortname + 'Policy', policy.format(shortname))

	# make the user
	response = conn.create_user(shortname)
	response = conn.add_user_to_group(shortname, shortname)
	response = conn.create_access_key(shortname)

	sleep(5) # give the access key a chance to propogate
	create_buckets(shortname=shortname)

@task
def setup(shortname=None):
	"""Setup 3 s3 buckets and tag them.  Pass shortname into task"""
	if shortname is None:
		log.error(red('You must provide a shortname'))
		return

	create_user(shortname);

<commit_msg>added some try blocks to allow for running through setup more than once, fixed a typo with the policy ...and spaces not tabs<commit_after>import boto
from fabric.colors import red, green
from fabric.api import task
from time import sleep

import logging
logging.basicConfig()
log = logging.getLogger(__name__)

def create_buckets(shortname):

    conn = boto.connect_s3()

    buckets = (shortname, shortname + '-staging', shortname + '-dev')
    tagset = boto.s3.tagging.TagSet()
    tagset.add_tag("project",shortname)

    tags = boto.s3.tagging.Tags()
    tags.add_tag_set(tagset)

    for bucket in buckets:
        try:
            conn.create_bucket(bucket)
        except:
            print red('problem creating bucket: {}'.format(bucket))
        bucket = conn.get_bucket(bucket)
        bucket.set_tags(tags)
        bucket.make_public(True)

policy = """{{
"Statement": [
        {{
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "arn:aws:s3:::*"
        }},
        {{
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::{0}-dev",
                "arn:aws:s3:::{0}-dev/*",
                "arn:aws:s3:::{0}-staging",
                "arn:aws:s3:::{0}-staging/*",
                "arn:aws:s3:::{0}-prod",
                "arn:aws:s3:::{0}-prod/*"

            ]
        }}
    ]
}}"""

def create_user(shortname=None):

    conn = boto.connect_iam()
    try:
        response = conn.create_group(shortname)
    except:
        print red('Problem creating group: {}'.format(shortname))
    print green(policy.format(shortname))
    response = conn.put_group_policy(shortname, shortname + 'Policy', policy.format(shortname))

    # make the user
    try:
        response = conn.create_user(shortname)
    except:
        print red('Problem creating user: {}'.format(shortname))
    response = conn.add_user_to_group(shortname, shortname)
    response = conn.create_access_key(shortname)

    sleep(5) # give the access key a chance to propogate
    create_buckets(shortname=shortname)

@task
def setup(shortname=None):
    """Setup 3 s3 buckets and tag them.  Pass shortname into task"""
    if shortname is None:
        log.error(red('You must provide a shortname'))
        return

    create_user(shortname);

<|endoftext|>


<commit_before>import re
import os
import glob
data_dir = '/data2/gmrtarch/cycle20/'
VALID_LIST = './parser/filter_healthy/healthy2_file.txt'
valid_observations = open(VALID_LIST, 'r').read().split('\n')[0:-1]
all_observations = os.listdir(data_dir)

def INVALID_OBS():
    for DIR_NAME in all_observations:
        current_obslog = glob.glob(data_dir+DIR_NAME+'/'+'*.obslog')
        """
        if current_obslog == []:
            print DIR_NAME
            break
        """
        #Extract substring that contains obslog relative path
        relative_path = re.findall(r'[/][\d]+[.]obslog', current_obslog[0])[0][1:]
        #Invalid file (not fitting given constraints i.e. < 900 MHz and IF BW != 6,16,32)
        if relative_path not in valid_observations:
            print data_dir+DIR_NAME
        #Valid obslog file with no LTA file in the DIR

        if relative_path in valid_observations:
            if glob.glob(data_dir+DIR_NAME+'/'+'*.lta') == []:
                print data_dir+DIR_NAME

def VALID_OBS():
    valid_obs = []
    for DIR_NAME in all_observations:
            current_obslog = glob.glob(data_dir+DIR_NAME+'/'+'*.obslog')

            #Extract substring that contains obslog relative path
            relative_path = re.findall(r'[/][\d]+[.]obslog', current_obslog[0])[0][1:]

            if relative_path in valid_observations:
                if glob.glob(data_dir+DIR_NAME+'/'+'*.lta') != []:
                    valid_obs.append(data_dir+DIR_NAME)
    return valid_obs
<commit_msg>Change relative path of healthy files in filter file<commit_after>import re
import os
import glob
data_dir = '/data2/gmrtarch/cycle20/'
VALID_LIST = '../parser/filter_healthy/healthy2_file.txt'
valid_observations = open(VALID_LIST, 'r').read().split('\n')[0:-1]
all_observations = os.listdir(data_dir)

def INVALID_OBS():
    for DIR_NAME in all_observations:
        current_obslog = glob.glob(data_dir+DIR_NAME+'/'+'*.obslog')
        """
        if current_obslog == []:
            print DIR_NAME
            break
        """
        #Extract substring that contains obslog relative path
        relative_path = re.findall(r'[/][\d]+[.]obslog', current_obslog[0])[0][1:]
        #Invalid file (not fitting given constraints i.e. < 900 MHz and IF BW != 6,16,32)
        if relative_path not in valid_observations:
            print data_dir+DIR_NAME
        #Valid obslog file with no LTA file in the DIR

        if relative_path in valid_observations:
            if glob.glob(data_dir+DIR_NAME+'/'+'*.lta') == []:
                print data_dir+DIR_NAME

def VALID_OBS():
    valid_obs = []
    for DIR_NAME in all_observations:
            current_obslog = glob.glob(data_dir+DIR_NAME+'/'+'*.obslog')

            #Extract substring that contains obslog relative path
            relative_path = re.findall(r'[/][\d]+[.]obslog', current_obslog[0])[0][1:]

            if relative_path in valid_observations:
                if glob.glob(data_dir+DIR_NAME+'/'+'*.lta') != []:
                    valid_obs.append(data_dir+DIR_NAME)
    return valid_obs
<|endoftext|>


<commit_before># vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)

import sys
import codecs

from unicodedata import east_asian_width, combining

from powerline.lib.encoding import get_preferred_output_encoding

try:
	from __builtin__ import unicode
except ImportError:
	unicode = str

try:
	from __builtin__ import unichr
except ImportError:
	unichr = chr

def u(s):
	'''Return unicode instance assuming UTF-8 encoded string.
	'''
	if type(s) is unicode:
		return s
	else:
		return unicode(s, 'utf-8')

if sys.version_info < (3,):
	def tointiter(s):
		'''Convert a byte string to the sequence of integers
		'''
		return (ord(c) for c in s)
else:
	def tointiter(s):
		'''Convert a byte string to the sequence of integers
		'''
		return iter(s)

def powerline_decode_error(e):
	if not isinstance(e, UnicodeDecodeError):
		raise NotImplementedError
	return (''.join((
		'<{0:02X}>'.format(c)
		for c in tointiter(e.object[e.start:e.end])
	)), e.end)

codecs.register_error('powerline_decode_error', powerline_decode_error)

last_swe_idx = 0

def register_strwidth_error(strwidth):
	global last_swe_idx
	last_swe_idx += 1

	def powerline_encode_strwidth_error(e):
		if not isinstance(e, UnicodeEncodeError):
			raise NotImplementedError
		return ('?' * strwidth(e.object[e.start:e.end]), e.end)

	ename = 'powerline_encode_strwidth_error_{0}'.format(last_swe_idx)
	codecs.register_error(ename, powerline_encode_strwidth_error)
	return ename

def out_u(s):
	'''Return unicode string suitable for displaying

	Unlike other functions assumes get_preferred_output_encoding() first. Unlike
	u() does not throw exceptions for invalid unicode strings. Unlike
	safe_unicode() does throw an exception if object is not a string.
	'''
	if isinstance(s, unicode):
		return s
	elif isinstance(s, bytes):
		return unicode(s, get_preferred_output_encoding(), 'powerline_decode_error')
	else:
		raise TypeError('Expected unicode or bytes instance, got {0}'.format(repr(type(s))))

def safe_unicode(s):
	'''Return unicode instance without raising an exception.

	Order of assumptions:
	* ASCII string or unicode object
	* UTF-8 string
	* Object with __str__() or __repr__() method that returns UTF-8 string or
	  unicode object (depending on python version)
	* String in powerline.lib.encoding.get_preferred_output_encoding() encoding
	* If everything failed use safe_unicode on last exception with which
	  everything failed
	'''
	try:
		try:
			if type(s) is bytes:
				return unicode(s, 'ascii')
			else:
				return unicode(s)
		except UnicodeDecodeError:
			try:
				return unicode(s, 'utf-8')
			except TypeError:
				return unicode(str(s), 'utf-8')
			except UnicodeDecodeError:
				return unicode(s, get_preferred_output_encoding())
	except Exception as e:
		return safe_unicode(e)

class FailedUnicode(unicode):
	'''Builtin ``unicode`` subclass indicating fatal error

	If your code for some reason wants to determine whether `.render()` method
	failed it should check returned string for being a FailedUnicode instance.
	Alternatively you could subclass Powerline and override `.render()` method
	to do what you like in place of catching the exception and returning
	FailedUnicode.
	'''
	pass

if sys.version_info < (3,):
	def string(s):
		if type(s) is not str:
			return s.encode('utf-8')
		else:
			return s
else:
	def string(s):
		if type(s) is not str:
			return s.decode('utf-8')
		else:
			return s

def surrogate_pair_to_character(high, low):
	'''Transform a pair of surrogate codepoints to one codepoint
	'''
	return 0x10000 + ((high - 0xD800) << 10) + (low - 0xDC00)

_strwidth_documentation = (
	'''Compute string width in display cells

	{0}

	:param dict width_data:
		Dictionary which maps east_asian_width property values to strings
		lengths. It is expected to contain the following keys and values (from
		`East Asian Width annex <http://www.unicode.org/reports/tr11/>`_):

		===  ======  ===========================================================
		Key  Value   Description
		===  ======  ===========================================================
		F    2       Fullwidth: all characters that are defined as Fullwidth in
		             the Unicode Standard [Unicode] by having a compatibility
		             decomposition of type <wide> to characters elsewhere in the
		             Unicode Standard that are implicitly narrow but unmarked.
		H    1       Halfwidth: all characters that are explicitly defined as
		             Halfwidth in the Unicode Standard by having a compatibility
		             decomposition of type <narrow> to characters elsewhere in
		             the Unicode Standard that are implicitly wide but unmarked,
		             plus U+20A9 ₩ WON SIGN.
		W    2       Wide: all other characters that are always wide. These
		             characters occur only in the context of East Asian
		             typography where they are wide characters (such as the
		             Unified Han Ideographs or Squared Katakana Symbols). This
		             category includes characters that have explicit halfwidth
		             counterparts.
		Na   1       Narrow: characters that are always narrow and have explicit
		             fullwidth or wide counterparts. These characters are
		             implicitly narrow in East Asian typography and legacy
		             character sets because they have explicit fullwidth or wide
		             counterparts. All of ASCII is an example of East Asian
		             Narrow characters.
		A    1 or 2  Ambigious: characters that may sometimes be wide and
		             sometimes narrow. Ambiguous characters require additional
		             information not contained in the character code to further
		             resolve their width. This information is usually defined in
		             terminal setting that should in turn respect glyphs widths
		             in used fonts. Also see :ref:`ambiwidth configuration
		             option <config-common-ambiwidth>`.
		N    1       Neutral characters: character that does not occur in legacy
		             East Asian character sets.
		===  ======  ===========================================================

	:param unicode string:
		String whose width will be calculated.

	:return: unsigned integer.''')

def strwidth_ucs_4(width_data, string):
	return sum(((
		(
			0
		) if combining(symbol) else (
			width_data[east_asian_width(symbol)]
		)
	) for symbol in string))

strwidth_ucs_4.__doc__ = _strwidth_documentation.format(
	'''This version of function expects that characters above 0xFFFF are
	represented using one symbol. This is only the case in UCS-4 Python builds.

	.. note:
		Even in UCS-4 Python builds it is possible to represent characters above
		0xFFFF using surrogate pairs. Characters represented this way are not
		supported.''')

def strwidth_ucs_2(width_data, string):
	return sum(((
		(
			width_data[east_asian_width(string[i - 1] + symbol)]
		) if 0xDC00 <= ord(symbol) <= 0xDFFF else (
			0
		) if combining(symbol) or 0xD800 <= ord(symbol) <= 0xDBFF else (
			width_data[east_asian_width(symbol)]
		)
	) for i, symbol in enumerate(string)))

strwidth_ucs_2.__doc__ = _strwidth_documentation.format(
	'''This version of function expects that characters above 0xFFFF are
	represented using two symbols forming a surrogate pair, which is the only
	option in UCS-2 Python builds. It still works correctly in UCS-4 Python
	builds, but is slower then its UCS-4 counterpart.''')
<commit_msg>Add documentation for `register_strwidth_error` and `string` functions<commit_after># vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)

import sys
import codecs

from unicodedata import east_asian_width, combining

from powerline.lib.encoding import get_preferred_output_encoding

try:
	from __builtin__ import unicode
except ImportError:
	unicode = str

try:
	from __builtin__ import unichr
except ImportError:
	unichr = chr

def u(s):
	'''Return unicode instance assuming UTF-8 encoded string.
	'''
	if type(s) is unicode:
		return s
	else:
		return unicode(s, 'utf-8')

if sys.version_info < (3,):
	def tointiter(s):
		'''Convert a byte string to the sequence of integers
		'''
		return (ord(c) for c in s)
else:
	def tointiter(s):
		'''Convert a byte string to the sequence of integers
		'''
		return iter(s)

def powerline_decode_error(e):
	if not isinstance(e, UnicodeDecodeError):
		raise NotImplementedError
	return (''.join((
		'<{0:02X}>'.format(c)
		for c in tointiter(e.object[e.start:e.end])
	)), e.end)

codecs.register_error('powerline_decode_error', powerline_decode_error)

last_swe_idx = 0

def register_strwidth_error(strwidth):
	'''Create new encode errors handling method similar to ``replace``

	Like ``replace`` this method uses question marks in place of the characters
	that cannot be represented in the requested encoding. Unlike ``replace`` the
	amount of question marks is identical to the amount of display cells
	offending character occupies. Thus encoding ``…`` (U+2026, HORIZONTAL
	ELLIPSIS) to ``latin1`` will emit one question mark, but encoding ``Ａ``
	(U+FF21, FULLWIDTH LATIN CAPITAL LETTER A) will emit two question marks.

	Since width of some characters depends on the terminal settings and
	powerline knows how to respect them a single error handling method cannot be
	used. Instead of it the generator function is used which takes ``strwidth``
	function (function that knows how to compute string width respecting all
	needed settings) and emits new error handling method name.

	:param function strwidth:
		Function that computs string width measured in display cells the string
		occupies when displayed.

	:return: New error handling method name.
	'''
	global last_swe_idx
	last_swe_idx += 1

	def powerline_encode_strwidth_error(e):
		if not isinstance(e, UnicodeEncodeError):
			raise NotImplementedError
		return ('?' * strwidth(e.object[e.start:e.end]), e.end)

	ename = 'powerline_encode_strwidth_error_{0}'.format(last_swe_idx)
	codecs.register_error(ename, powerline_encode_strwidth_error)
	return ename

def out_u(s):
	'''Return unicode string suitable for displaying

	Unlike other functions assumes get_preferred_output_encoding() first. Unlike
	u() does not throw exceptions for invalid unicode strings. Unlike
	safe_unicode() does throw an exception if object is not a string.
	'''
	if isinstance(s, unicode):
		return s
	elif isinstance(s, bytes):
		return unicode(s, get_preferred_output_encoding(), 'powerline_decode_error')
	else:
		raise TypeError('Expected unicode or bytes instance, got {0}'.format(repr(type(s))))

def safe_unicode(s):
	'''Return unicode instance without raising an exception.

	Order of assumptions:
	* ASCII string or unicode object
	* UTF-8 string
	* Object with __str__() or __repr__() method that returns UTF-8 string or
	  unicode object (depending on python version)
	* String in powerline.lib.encoding.get_preferred_output_encoding() encoding
	* If everything failed use safe_unicode on last exception with which
	  everything failed
	'''
	try:
		try:
			if type(s) is bytes:
				return unicode(s, 'ascii')
			else:
				return unicode(s)
		except UnicodeDecodeError:
			try:
				return unicode(s, 'utf-8')
			except TypeError:
				return unicode(str(s), 'utf-8')
			except UnicodeDecodeError:
				return unicode(s, get_preferred_output_encoding())
	except Exception as e:
		return safe_unicode(e)

class FailedUnicode(unicode):
	'''Builtin ``unicode`` subclass indicating fatal error

	If your code for some reason wants to determine whether `.render()` method
	failed it should check returned string for being a FailedUnicode instance.
	Alternatively you could subclass Powerline and override `.render()` method
	to do what you like in place of catching the exception and returning
	FailedUnicode.
	'''
	pass

if sys.version_info < (3,):
	def string(s):
		if type(s) is not str:
			return s.encode('utf-8')
		else:
			return s
else:
	def string(s):
		if type(s) is not str:
			return s.decode('utf-8')
		else:
			return s

string.__doc__ = (
	'''Transform ``unicode`` or ``bytes`` object into ``str`` object

	On Python-2 this encodes ``unicode`` to ``bytes`` (which is ``str``) using
	UTF-8 encoding; on Python-3 this decodes ``bytes`` to ``unicode`` (which is
	``str``) using UTF-8 encoding.

	Useful for functions that expect an ``str`` object in both unicode versions,
	not caring about the semantic differences between them in Python-2 and
	Python-3.
	'''
)

def surrogate_pair_to_character(high, low):
	'''Transform a pair of surrogate codepoints to one codepoint
	'''
	return 0x10000 + ((high - 0xD800) << 10) + (low - 0xDC00)

_strwidth_documentation = (
	'''Compute string width in display cells

	{0}

	:param dict width_data:
		Dictionary which maps east_asian_width property values to strings
		lengths. It is expected to contain the following keys and values (from
		`East Asian Width annex <http://www.unicode.org/reports/tr11/>`_):

		===  ======  ===========================================================
		Key  Value   Description
		===  ======  ===========================================================
		F    2       Fullwidth: all characters that are defined as Fullwidth in
		             the Unicode Standard [Unicode] by having a compatibility
		             decomposition of type <wide> to characters elsewhere in the
		             Unicode Standard that are implicitly narrow but unmarked.
		H    1       Halfwidth: all characters that are explicitly defined as
		             Halfwidth in the Unicode Standard by having a compatibility
		             decomposition of type <narrow> to characters elsewhere in
		             the Unicode Standard that are implicitly wide but unmarked,
		             plus U+20A9 ₩ WON SIGN.
		W    2       Wide: all other characters that are always wide. These
		             characters occur only in the context of East Asian
		             typography where they are wide characters (such as the
		             Unified Han Ideographs or Squared Katakana Symbols). This
		             category includes characters that have explicit halfwidth
		             counterparts.
		Na   1       Narrow: characters that are always narrow and have explicit
		             fullwidth or wide counterparts. These characters are
		             implicitly narrow in East Asian typography and legacy
		             character sets because they have explicit fullwidth or wide
		             counterparts. All of ASCII is an example of East Asian
		             Narrow characters.
		A    1 or 2  Ambigious: characters that may sometimes be wide and
		             sometimes narrow. Ambiguous characters require additional
		             information not contained in the character code to further
		             resolve their width. This information is usually defined in
		             terminal setting that should in turn respect glyphs widths
		             in used fonts. Also see :ref:`ambiwidth configuration
		             option <config-common-ambiwidth>`.
		N    1       Neutral characters: character that does not occur in legacy
		             East Asian character sets.
		===  ======  ===========================================================

	:param unicode string:
		String whose width will be calculated.

	:return: unsigned integer.''')

def strwidth_ucs_4(width_data, string):
	return sum(((
		(
			0
		) if combining(symbol) else (
			width_data[east_asian_width(symbol)]
		)
	) for symbol in string))

strwidth_ucs_4.__doc__ = _strwidth_documentation.format(
	'''This version of function expects that characters above 0xFFFF are
	represented using one symbol. This is only the case in UCS-4 Python builds.

	.. note:
		Even in UCS-4 Python builds it is possible to represent characters above
		0xFFFF using surrogate pairs. Characters represented this way are not
		supported.''')

def strwidth_ucs_2(width_data, string):
	return sum(((
		(
			width_data[east_asian_width(string[i - 1] + symbol)]
		) if 0xDC00 <= ord(symbol) <= 0xDFFF else (
			0
		) if combining(symbol) or 0xD800 <= ord(symbol) <= 0xDBFF else (
			width_data[east_asian_width(symbol)]
		)
	) for i, symbol in enumerate(string)))

strwidth_ucs_2.__doc__ = _strwidth_documentation.format(
	'''This version of function expects that characters above 0xFFFF are
	represented using two symbols forming a surrogate pair, which is the only
	option in UCS-2 Python builds. It still works correctly in UCS-4 Python
	builds, but is slower then its UCS-4 counterpart.''')
<|endoftext|>


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

from fabric.api import *
import os
import re

# What does this release script do:
# get major.minor.micro from setup.py
# Chop off -dev tag at the end. If no dev at the end, bump the micro
# version
# commit it.
# Tag at the commit.
# Bump to the next dev version: major.minor.micro+1'-dev'
# commit it.
# Push changes and tags to remote

# How do you use a release like this:
# Get it from the GitHub by using the tag + tar ball feature

versionTemplates = {
        'git-tag': 'v%(major)s.%(minor)s.%(micro)s'
        , 'git-message': 'Release Version %(major)s.%(minor)s.%(micro)s'
        , 'dev-message': 'Bump Version to %(major)s.%(minor)s.%(micro)s-dev'
        }

# Monkey-patch "open" to honor fabric's current directory
_old_open = open
def open(path, *args, **kwargs):
    return _old_open(os.path.join(env.lcwd, path), *args, **kwargs)

def _validateVersion(v):
    versionRe = re.compile('^(?P<major>[0-9]+)\\.(?P<minor>[0-9]+)\\.(?P<micro>[0-9]+)(?P<pre>[-0-9a-zA-Z]+)?$')
    m = versionRe.match(v)
    if not m:
        raise Exception('Version must be in the format <number>.<number>.<number>[<string>]')

    valDict = m.groupdict()
    for k in ('major', 'minor', 'micro'): valDict[k] = int(valDict[k])
    return valDict

# Decorator class to fab target
class _cloneDir(object):
    def __init__(self, gitUrl, project, default_branch):
        self.gitUrl = gitUrl
        self.project = project
        self.default_branch = default_branch

    def __call__(self, f):
        def wrapped_f(*args, **kwargs):
            local('rm -rf ../tmpfab')
            local('mkdir ../tmpfab')
            local('git clone %s ../tmpfab/%s' % (self.gitUrl, self.project))

            with lcd(os.path.join('..', 'tmpfab', self.project)):
                branch = prompt('Please enter release branch:',
                    default=self.default_branch)
                local('git checkout %s' % branch)
                kwargs['branch'] = branch
                f(*args, **kwargs)
            local('rm -rf ../tmpfab')
        return wrapped_f

def _getReleaseVersion():
    # Get current python version
    currentVersionStr = local('cat VERSION.txt', capture=True)

    cvd = _validateVersion(currentVersionStr)
    if not currentVersionStr.endswith('-dev'):
        cvd['micro'] += 1

    nextVersionStr = '%d.%d.%d' % (cvd['major'], cvd['minor'], cvd['micro'])
    print 'You current version is %s.  You release version will be %s' % (currentVersionStr, nextVersionStr)

    return cvd

def _gitTag(version, branch='develop'):
    versionTag = versionTemplates['git-tag'] % version
    versionMsg = versionTemplates['git-message'] % version

    comment = prompt('Optional comment for this release:', default='')
    if comment != '':
        versionMsg += ': ' + comment
    local('git commit -am "%s"' % (versionMsg))
    commit = local('git rev-parse --short HEAD', capture=True)
    local('git tag -af %s -m "%s" %s' % (versionTag, versionMsg, commit))

    print versionTag, versionMsg, commit

@_cloneDir(gitUrl='git@github.com:ooici/ion-ux.git',
    project='ion-ux',
    default_branch='master')
def release(branch):

    # Deduce release version
    nextVersionD = _getReleaseVersion()

    versionStr = '%d.%d.%d' % (nextVersionD['major'], nextVersionD['minor'],
            nextVersionD['micro'])
    local('echo %s > VERSION.txt' % versionStr)

    # Tag at release version
    _gitTag(nextVersionD, branch=branch)

    # Immediately go to next dev version to ensure release version is tied
    # to one commit only
    nextVersionD['micro'] += 1
    versionStr = '%d.%d.%d' % (nextVersionD['major'], nextVersionD['minor'],
            nextVersionD['micro'])
    local('echo %s-dev > VERSION.txt' % versionStr)

    devMsg = versionTemplates['dev-message'] % nextVersionD
    local('git commit -am "%s"' % devMsg)

    remote = 'origin'

    # Push commits and tags
    local('git push %s --tags' % (remote))
    local('git push %s %s' % (remote, branch))
<commit_msg>Use local clone for fab release file<commit_after>#!/usr/bin/env python

from fabric.api import *
import os
import re

# What does this release script do:
# get major.minor.micro from setup.py
# Chop off -dev tag at the end. If no dev at the end, bump the micro
# version
# commit it.
# Tag at the commit.
# Bump to the next dev version: major.minor.micro+1'-dev'
# commit it.
# Push changes and tags to remote

# How do you use a release like this:
# Get it from the GitHub by using the tag + tar ball feature

versionTemplates = {
        'git-tag': 'v%(major)s.%(minor)s.%(micro)s'
        , 'git-message': 'Release Version %(major)s.%(minor)s.%(micro)s'
        , 'dev-message': 'Bump Version to %(major)s.%(minor)s.%(micro)s-dev'
        }

# Monkey-patch "open" to honor fabric's current directory
_old_open = open
def open(path, *args, **kwargs):
    return _old_open(os.path.join(env.lcwd, path), *args, **kwargs)

def _validateVersion(v):
    versionRe = re.compile('^(?P<major>[0-9]+)\\.(?P<minor>[0-9]+)\\.(?P<micro>[0-9]+)(?P<pre>[-0-9a-zA-Z]+)?$')
    m = versionRe.match(v)
    if not m:
        raise Exception('Version must be in the format <number>.<number>.<number>[<string>]')

    valDict = m.groupdict()
    for k in ('major', 'minor', 'micro'): valDict[k] = int(valDict[k])
    return valDict

# Decorator class to fab target
class _cloneDir(object):
    def __init__(self, gitUrl, project, default_branch):
        self.gitUrl = gitUrl
        self.project = project
        self.default_branch = default_branch

    def __call__(self, f):
        def wrapped_f(*args, **kwargs):
            local('rm -rf ../tmpfab')
            local('mkdir ../tmpfab')
            local('git clone . ../tmpfab/%s' % self.project)
            remote = 'fab'
            with lcd(os.path.join('..', 'tmpfab', self.project)):
                branch = prompt('Please enter release branch:',
                    default=self.default_branch)
                local('git remote add %s %s' % (remote, self.gitUrl))
                local('git fetch %s' % remote)

                local('git checkout -b fab_%s %s/%s' % (branch, remote, branch))
                kwargs['branch'] = branch
                kwargs['remote'] = remote
                f(*args, **kwargs)
            local('rm -rf ../tmpfab')
        return wrapped_f

def _getReleaseVersion():
    # Get current python version
    currentVersionStr = local('cat VERSION.txt', capture=True)

    cvd = _validateVersion(currentVersionStr)
    if not currentVersionStr.endswith('-dev'):
        cvd['micro'] += 1

    nextVersionStr = '%d.%d.%d' % (cvd['major'], cvd['minor'], cvd['micro'])
    print 'You current version is %s.  You release version will be %s' % (currentVersionStr, nextVersionStr)

    return cvd

def _gitTag(version):
    versionTag = versionTemplates['git-tag'] % version
    versionMsg = versionTemplates['git-message'] % version

    comment = prompt('Optional comment for this release:', default='')
    if comment != '':
        versionMsg += ': ' + comment
    local('git commit -am "%s"' % (versionMsg))
    commit = local('git rev-parse --short HEAD', capture=True)
    local('git tag -af %s -m "%s" %s' % (versionTag, versionMsg, commit))

    print versionTag, versionMsg, commit

@_cloneDir(gitUrl='git@github.com:ooici/ion-ux.git',
    project='ion-ux',
    default_branch='master')
def release(branch, remote='fab'):

    # Deduce release version
    nextVersionD = _getReleaseVersion()

    versionStr = '%d.%d.%d' % (nextVersionD['major'], nextVersionD['minor'],
            nextVersionD['micro'])
    local('echo %s > VERSION.txt' % versionStr)

    # Tag at release version
    _gitTag(nextVersionD)

    # Immediately go to next dev version to ensure release version is tied
    # to one commit only
    nextVersionD['micro'] += 1
    versionStr = '%d.%d.%d' % (nextVersionD['major'], nextVersionD['minor'],
            nextVersionD['micro'])
    local('echo %s-dev > VERSION.txt' % versionStr)

    devMsg = versionTemplates['dev-message'] % nextVersionD
    local('git commit -am "%s"' % devMsg)

    # Push commits and tags
    local('git push %s --tags' % (remote))
    local('git push %s HEAD:%s' % (remote, branch))
<|endoftext|>