def sanitized_facets(input_facets): """ Sanitize all the facets. It must be a dictionary of scalar values, or a dictionary of lists of scalar values. e.g. {'derp': 123, 'merp': [123, "hello"]} """ if type(input_facets) != dict: raise ValidationError('"facets" must be dictionary') facets = [] for facet_name, facet_values in input_facets.items(): if type(facet_name) not in [str, unicode]: raise ValidationError("Facet names must be strings") facet_name = to_utf8_str(facet_name) if type(facet_values) in [str, unicode, int, float]: facet_values = [to_utf8_str(facet_values)] if type(facet_values) not in [list, set]: raise ValidationError("Facet values for '%s' must be a string, int or float" " - or a list containing only strings, ints and floats" % (facet_name,)) for i in range(0, len(facet_values)): value = facet_values[i] if type(value) not in [int, str, unicode, float]: raise ValidationError("Facet '%s[%d]' must be a string, int or float" % (facet_name, i)) facets.append((facet_name, [to_utf8_str(tmp_value) for tmp_value in facet_values])) return sorted(facets, key=lambda x: x[0])
def sanitized_values(input_values): """ Sanitize all values, it must be a dictionary of values """ if type(input_values) != dict: raise ValidationError('"values" must be dictionary') values = [] for value_name, value in input_values.items(): if type(value_name) not in [str, unicode]: raise ValidationError("Value names must be strings") if type(value) != int: raise ValidationError("Value '%s' must be an integer" % (value_name,)) values.append((to_utf8_str(value_name), int(value))) return sorted(values, key=lambda x: x[0])
def make_record(data): """ Make a record to be inserted into a bucket """ if data is None: raise ValidationError('No data received') if type(data) != dict or 'facets' not in data or 'values' not in data: raise ValidationError('Data must be dictionary with "id", "facets" and "values" keys') if 'id' not in data or type(data['id']) not in [int, str, unicode]: raise ValidationError('The "id" must be a string or int') record_id = to_utf8_str(data['id']) facets = sanitized_facets(data['facets']) values = sanitized_values(data['values']) return { 'id': record_id, 'facets': facets, 'values': values, }
def find_values(bucket): """ Retrieves a list of the facet values underneath a parent facet. It accepts a JSON dictionry of the parent facet names and a limit on how many sub-facets to return. { 'tablet-days': {'facet': {'device': ['tablet', 'apple'], 'time': ['2013', '1', '15']}, 'limit': 50} } On success it will return the a standard API response including the 'results' key which contains a { 'ok': true, 'status': 200, 'results': { 'tablet-days': {'1': {'duration': 38289323, 'datapoints': 4919}}, {'2': {'duration': 382829, 'datapoints': 1234}} } } """ start_time = unixtime() request = bottle.request query = request.json if query is None or type(query) != dict: bottle.abort(400, 'Must POST application/json dictionary') # Validate the searches searches = {} for name, search in query.items(): if 'facet' not in search: bottle.abort(400, '"facet" key required for "%s"' % (name,)) if 'limit' not in search: bottle.abort(400, '"limit" key required for "%s"' % (name,)) try: limit = int(search.get('limit')) except ValueError: bottle.abort(400, 'Invalid "limit" for "%s"' % (name,)) withvalues = bool(search.get('withvalues')) startkey = None if 'startkey' in search: try: startkey = to_utf8_str(search['startkey']) except TypeError: bottle.abort(400, 'Start key for "%s" is invalid type' % (name,)) try: facet = sanitized_facets(search['facet']) except ValidationError, oops: LOG.info("'%s' contained invalid facet", name, exc_info=True) bottle.abort(400, "%s: %s" % (name, oops.message)) searches[name] = { 'facet': split_facet(facet), 'limit': limit, 'startkey': start, 'withvalues': withvalues }