Files
2016-11-01 13:24:49 -04:00

203 lines
6.6 KiB
Python

## **************
## Copyright 2015 MIT Lincoln Laboratory
## Project: PACE
## Authors: ATLH, SS
## Description: Common utility functions for testing the code
## Modifications:
## Date Name Modification
## ---- ---- ------------
## 20 Jan 2015 ATLH Original file - based on sign_utils
## 15 Dec 2015 SS Added functionality of data generation
## **************
""" Utility functions for testing PACE software with rows written to
Accumulo.
Any randomized test should use the same seed so that they are equivalent.
"""
import os
import sys
this_dir = os.path.dirname(os.path.dirname(__file__))
base_dir = os.path.join(this_dir, '../..')
sys.path.append(base_dir)
import random
import copy
import time, datetime
import string
from pyaccumulo import Range
class Timer(object):
"""
Timer that works with python's 'with' syntax
measures the time taken in seconds and milliseconds
"""
#modified from http://www.huyng.com/posts/python-performance-analysis/
def __enter__(self):
self.start = time.clock()
return self
def __exit__(self, *args):
self.end = time.clock()
self.secs = self.end - self.start
self.msecs = self.secs * 1000 # millisecs
def generate_data(filename,
seed,
vis=False,
default_vis="default",
vis_in_value=False,
num_entries=50000,
num_rows=1000,
vis_length=9,
row_length=9):
""" Generate a tab-delimited text file with five columns:
row, col fam, col qual, col vis, value
keyword arguments:
filename - name of file to which the data will be written
seed - a string to be used as the seed for the random number generator
visibility - if True, random column visibilities are added. If False,
the default visibility string is added. (default False)
default_vis - the default visibility string to use if 'visibiilty' is
set to False. (default "default")
vis_in_value - if True, a random "column visibility" is generated, but
this is appended to the value rather than column visibility. If
True, the param vis is treated as False.
num_entries - Number of entries written.
num_rows - Number of rows in the data written.
vis_length - The length (in chars) of the random col visibility added
(true length will be this + 2)
row_length - Number of chars in each row ID. Numbers are zero padded to
reach this length.
"""
# seed RNG
random.seed(seed)
rows = map(lambda x: str(x).zfill(row_length), \
random.sample(xrange(10 ** row_length), num_rows))
unused_rows = copy.deepcopy(rows)
def generate_entry():
""" Helper function for generate_data that returns a single entry
which will form a single line of the data file
"""
def generate_field(length=row_length):
""" Generates a single field (row, col fam, or col qual) randomly
"""
return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(length))
def generate_row():
""" Generates a single rowID from the list of rows. On successive
calls, ensures each rowID is used at least once, but makes no
other guarantees about how many times each rowID is used
"""
return unused_rows.pop() if unused_rows else random.choice(rows)
def generate_colvis():
""" Generates a single column visibility (either the default or a
random value)
"""
return default_vis if (vis_in_value or not vis) \
else generate_field(vis_length).zfill(vis_length)
def generate_value():
""" Generates a random value, accounting for vis_in_value """
val = generate_field()
return val + generate_field(vis_length).zfill(vis_length) if vis_in_value else val
return generate_row() + "\t" + \
generate_field() + "\t" + \
generate_field() + "\t" + \
generate_colvis() + "\t" + \
generate_value() + "\n"
# Open file and generate data
with open(filename, "w") as f:
for i in range(num_entries):
f.write(generate_entry())
def sanitize(string):
""" Turns '-' into '_' for accumulo table names
"""
return string.replace('-', '_')
def get_single_entry(conn, table, row=None, cf=None, cq=None):
""" Get the single entry with the given key fields (row, column
family, and column qualifier) from an Accumulo database.
Returns:
None if no entries were found
The found entry if exactly one was found
Raises an assertion error if more than one entry was found
"""
if cf is None:
assert cq is None
cols = None
elif cq is None:
cols = [[cf]]
else:
cols = [[cf, cq]]
candidates = conn.scan(table, Range(srow=row, erow=row), cols=cols)
try:
first = next(candidates)
except StopIteration:
# No candidates; return None
return None
try:
more = next(candidates)
assert False # only supposed to have one result
except StopIteration:
return first
def entry_exists(conn, table, row, cf=None, cq=None):
""" Check to see if at least one entry with the given information
exists in the given Accumulo table. Takes at least a row,
and possibly a column family & qualifier as well.
Arguments:
conn : Accumulo - a connection to the Accumulo instance to check
table : string - the name of the table to check
row : string - the name of the row to look for within the given table
cf : optional string - the column family to look for, if desired
cq : optional string - the column qualifier to look for, if desired
Returns:
bool - whether there is at least one entry in the given connection with
the given fields
Assumptions:
- The given table exists on the connection
- If cf is not specified/None, then neither is cq
"""
if cf is None:
assert cq is None
cols = None
elif cq is None:
cols = [[cf]]
else:
cols = [[cf, cq]]
candidates = conn.scan(table, Range(srow=row, erow=row), cols=cols)
try:
first = next(candidates)
except StopIteration:
# Nothing found
return False
else:
# Something found
return True