def items_exist(): params = request.json type = unicode(params.get('type', '')) updated_after_str = params.get('updated_after') ids_list = params.get('ids', []) res = _items_exist(type, ids_list, updated_after_str) return jsonxfy(status='ok', exist=res)
def item_exists(): type = unicode(request.args.get('type', '')) updated_after_str = request.args.get('updated_after') id = request.args.get('id') if id is None: return abort(405) res = _items_exist(type, [id], updated_after_str) return jsonxfy(status='ok', exists=res[id])
def query(): filters = [] active_str = request.args.get('$active', '') if active_str: if active_str.lower() in ('1','t','true'): filters.append(app.table.c.expires_on >= datetime.datetime.utcnow()) else: filters.append(app.table.c.expires_on < datetime.datetime.utcnow()) for t in ('before','after'): arg_str = request.args.get('$seen_' + t) if arg_str: dt = parse_dt(arg_str) if t == 'before': filters.append(app.table.c.first_seen_on < dt) else: filters.append(app.table.c.last_seen_on > dt) for key in ('id', 'type'): in_key = key + '[]' if in_key in request.args: vals = request.args.getlist(in_key) filters.append(getattr(app.table.c, key).in_(vals)) for in_key, vals in request.args.iterlists(): if not in_key.endswith('[]') or not vals: continue fieldname = in_key[:-2] if fieldname in ('id', 'type'): continue ored_filters = [] for val in vals: ored_filters.append(app.table.c.attributes.op('->>')(fieldname) == unicode(val)) if ored_filters: filters.append(reduce(or_, ored_filters)) items = [] if filters: where = reduce(and_, filters) else: where = None for row in app.db.execute(select([app.table], where)): item = {} items.append(item) for key in ('id', 'type', 'attributes'): item[key] = row[key] return jsonxfy(status='ok', items=items)
def record_items(): items = request.json.get('items') messages = [] if not isinstance(items, list): return jsonxfy(status='error', messages=['Invalid JSON body']) for i, item in enumerate(items): # ID try: id = item['id'] except KeyError: messages.append('Object %i: missing id' % i) continue # Type type = item.get('type') if type is None: type = u'' if not isinstance(type, unicode): type = unicode(type) # Partial update partial_update = item.get('__partial_update__', False) # Datetime dt = item.get('__dt__') or item.get('__visited_on__') if dt is None or not isinstance(dt, basestring): dt = datetime.datetime.utcnow().isoformat() # TTL ttl = item.get('__ttl__') if ttl is not None and isinstance(ttl, (int, float, long)): expires_on = (datetime.datetime.utcnow() + datetime.timedelta(seconds=ttl)) else: expires_on = (datetime.datetime.utcnow() + app.config.get('DEFAULT_TTL', datetime.timedelta(days=14))) # Launch a transaction, retry in case of duplicate errors while 1: with app.db.begin_nested() as trans: # Get the original row row = app.db.execute(app.table.select( ((app.table.c.id == id) & (app.table.c.type == type)), for_update=True)).fetchone() if row is None: obj = { 'uuid': str(uuid.uuid4()), 'type': type, 'id': id, 'first_seen_on': dt, 'attributes': {}, 'history': {} } else: obj = dict(row.items()) obj['last_seen_on'] = dt obj['expires_on'] = expires_on obj['active'] = True if not partial_update: obj['fully_updated_on'] = dt if obj['attributes'] is None: obj['attributes'] = {} if obj['history'] is None: obj['history'] = {} for k,v in item.iteritems(): if k in ('id','type') or k.startswith('__'): continue history = k.startswith('$') if history: k = k[1:] try: attr_hist = obj['history'][k][:] except KeyError: attr_hist = obj['history'][k] = [] insert_index = 0 for i, (h_dt, h_val) in enumerate(attr_hist): if h_dt > dt: insert_index = i break else: insert_index = len(attr_hist) if insert_index > 0: prev_dt, prev_val = attr_hist[insert_index - 1] if prev_val == v: # Don't insert, the previous value is identical insert_index = None if insert_index is not None and insert_index < len(attr_hist): next_dt, next_val = attr_hist[insert_index] if next_val == v: insert_index = None if insert_index is not None: attr_hist.insert(insert_index, (dt, v)) obj['history'][k] = attr_hist v = attr_hist[-1][1] obj['attributes'][k] = v jsonified = dict(obj) jsonified['attributes'] = json.dumps(obj['attributes']) jsonified['history'] = json.dumps(obj['history']) if row is None: try: app.db.execute(app.table.insert(obj)) except DatabaseError, e: if is_duplicate_error(e): trans.rollback() continue raise else: app.db.execute(app.table.update( whereclause=(app.table.c.uuid == row['uuid']), values=obj)) break
if row is None: try: app.db.execute(app.table.insert(obj)) except DatabaseError, e: if is_duplicate_error(e): trans.rollback() continue raise else: app.db.execute(app.table.update( whereclause=(app.table.c.uuid == row['uuid']), values=obj)) break return jsonxfy(status='ok', messages=messages) def _items_exist(type, ids_list, updated_after_str): if not isinstance(ids_list, list): return abort(400) ids = map(unicode, ids_list) res = dict((id, False) for id in ids) where = app.table.c.id.in_(ids) if updated_after_str: where &= (app.table.fully_updated_on > parse_dt(updated_after_str)) res.update(r for r in app.db.execute(select([app.table.c.id, True], where))) return res @api_blueprint.route('/items_exist', methods=['POST'])