async def post_document(request, parent=None): payload = request.json if "pid" not in payload or not isinstance(payload["pid"], str): return response.json({"error": "need_pid_string"}, status=400) if "title" not in payload or not isinstance(payload["title"], str): return response.json({"error": "need_title_string"}, status=400) if "published" in payload and not isinstance(payload["published"], bool): return response.json({"error": "invalid_published_type"}, status=400) async with pg.transaction() as conn: node = await conn.fetchrow(t_document_hist.insert().values( pid=payload["pid"], title=payload["title"], published=payload.get("published", False), ).returning(t_document_hist.c.hid, t_document_hist.c.tstamp)) edge = await conn.fetchrow(t_document_event.insert().values( parent=parent, hist=node["hid"], uid=request["session"]["uid"], reason="insert" if parent is None else "update", ).returning(t_document_event.c.tstamp)) return response.json({ "hid": str(node["hid"]), "content_tstamp": node["tstamp"] and node["tstamp"].strftime("%Y-%m-%dT%H:%M:%S.%fZ"), "action_tstamp": edge["tstamp"].strftime("%Y-%m-%dT%H:%M:%S.%fZ"), })
async def put_edge_event(request, parent=None, hist=None): async with pg.transaction() as conn: await conn.execute(t_document_event.delete().where( (t_document_event.c.parent == parent) & (t_document_event.c.hist == hist) )) await conn.fetchrow(t_document_event.insert().values( parent=parent, hist=hist, **request.json # TODO: validate the input )) return response.json({"status": "replaced"})
async def put_node(request, hid): async with pg.transaction() as conn: await conn.execute( t_document_hist.delete().where(t_document_hist.c.hid == hid) ) node = await conn.fetchrow(t_document_hist.insert().values( **request.json # TODO: validate the input ).returning(t_document_hist.c.hid)) return response.json({ "status": "replaced", "hid": str(node["hid"]), })
async def increment_metrics(feature: str, env: str, date_: date): upsert = insert(db.metrics).values(feature=feature, env=env, date=date_) \ .on_conflict_do_update( index_elements=[db.metrics.c.env, db.metrics.c.feature, db.metrics.c.date], set_={'hit_count': text('metrics.hit_count + 1')} ) try: async with pg.transaction() as conn: await conn.fetch(upsert) except Exception: traceback.print_exc() pass
async def dislike_channel(client: ClientConnection, message: dict): # TODO: must be merged with like_channel according to new API if not client.is_initialised(): API._log("Uninitialised client tried to dislike a channel") await client.send_error( message['id'], 401, "client must initialise before attempting to dislike a channel" ) return sel_q = select([Channel ]).where(Channel.username == message["username"]) channel = await pg.fetchrow(sel_q) if channel is None: await client.send_error(message['id'], 404, "channel does not exist") return # TODO: take user's IP into account to make fake likes adding harder or allow to like only authorized users # TODO: There is an error - can't undo like or dislike async with pg.transaction() as conn: sel_q = select([ChannelSessionAction]). \ where(and_(ChannelSessionAction.channel_id == channel['id'], ChannelSessionAction.session_id == client.session['id'])).with_for_update() channel_session_action = await conn.fetchrow(sel_q) if channel_session_action is None: ins_q = insert(ChannelSessionAction).values( channel_id=channel['id'], session_id=client.session['id'], like=False) await conn.fetchrow(ins_q) else: if not channel_session_action['like']: await client.send_error(message['id'], 403, "channel already disliked") return upd_q = update(ChannelSessionAction).where(ChannelSessionAction.id == channel_session_action['id']). \ values(like=False) await conn.fetchrow(upd_q) upd_q = update(Channel).where(Channel.id == channel['id']).values( likes=Channel.likes - 1) await conn.fetchrow(upd_q) API._log(f'Disliked channel {message["username"]}') await client.send_response({ 'id': message['id'], 'action': message['action'] })
async def set_toggle_state(env, feature, state, rollout_days=0): results = await pg.fetch(db.toggles.select().where( db.toggles.c.feature == feature).where(db.toggles.c.env == env)) reply = {'toggle': {'env': env, 'feature': feature, 'state': state}} if results and results[0]['state'] == state: # nothing to do return reply elif not results and state == 'OFF': # nothing to do return reply # delete the toggle first to clean up any data async with pg.transaction() as conn: await conn.fetchval(db.toggles.delete().where( db.toggles.c.feature == feature).where(db.toggles.c.env == env)) schedule = {} if state == 'ROLL': schedule['increment'] = 1 schedule['hours_count'] = 0 schedule['total_hours'] = int(float(rollout_days)) * 24 schedule['current_percent'] = 1 elif state == 'PAUSE': roll_schedule = json.loads(results[0]['schedule']) schedule['hours_count'] = 0 schedule['total_hours'] = 48 schedule['current_percent'] = roll_schedule.get('current_percent') schedule['rolling_state'] = roll_schedule else: schedule['dirty'] = True if state in ('ON', 'ROLL', 'PAUSE'): await conn.fetchval(db.toggles.insert().values( feature=feature, env=env, state=state, date_on=functions.now(), schedule=schedule)) elif state == 'OFF': # nothing to do, already deleted pass return reply
async def get_graph(request, hid): cte_query = t_document_event.select() \ .where(t_document_event.c.hist == str(hid)) \ .cte("all_events", recursive=True) ref_query = cte_query.alias("ref") evt_query = t_document_event.alias("evt") full_events_query = cte_query.union( select( columns=[evt_query], from_obj=[ref_query, evt_query], whereclause=(evt_query.c.parent == ref_query.c.hist) | (evt_query.c.hist == ref_query.c.parent), ) ).select().order_by(cte_query.c.tstamp) async with pg.transaction(isolation="repeatable_read") as conn: edges = await conn.fetch(full_events_query) nodes = await conn.fetch(t_document_hist.select().where( t_document_hist.c.hid.in_([r["hist"] for r in edges]) ).order_by(t_document_hist.c.tstamp)) return response.json({ "nodes": [{ "hid": str(node["hid"]), "pid": node["pid"], "title": node["title"], "tstamp": node["tstamp"] and node["tstamp"].strftime("%Y-%m-%dT%H:%M:%S.%fZ"), } for node in nodes], "edges": [{ "parent": edge["parent"] and str(edge["parent"]), "hist": str(edge["hist"]), "reason": edge["reason"], "comment": edge["comment"], "tstamp": edge["tstamp"].strftime("%Y-%m-%dT%H:%M:%S.%fZ"), } for edge in edges], })
async def test_transaction(): await _init() async with pg.transaction() as conn: for row in await conn.fetch(query): assert row.a == 4.0
async def modify_channel(client: ClientConnection, message: dict): """ Get available channel tags list :param client: :param dict message: """ # Check is user authorized if not client.is_authorized(): await client.send_error( message['id'], 401, "client must login before attempting to modify a channel") return # Check channel exists in our DB and verified # Check user is admin sel_q = select([Channel]).\ select_from(outerjoin(Channel, ChannelAdmin)).\ where(and_(Channel.username == message['username'], Channel.verified == True, ChannelAdmin.admin_id == client.session['client_id'])) API._log('DBG: %s' % sel_q) res = await pg.fetch(sel_q) if not res: await client.send_error( message['id'], 404, 'Channel is not found or not verified or user is not admin') return channel = dict(res.items()) updated = {} # Check category change if 'category_id' in message: updated['category_id'] = message['category_id'] # Check mutual promotion changed if 'mut_promo' in message: updated['mutual_promotion'] = message['mut_promo'] # Check cost changed if 'cost' in message: updated['cost'] = message['cost'] # Check language changed if 'language' in message: updated['language'] = message['language'] # Check description changed if 'description' in message: updated['description'] = message['description'] # Check tags changed if 'tags' in message: # TODO: custom tags can be defined for premium channels # istartswith analog here sel_q = select([Tag]).select_from(Tag).\ where(func.lower(Tag.name).in_([tag.lower() for tag in message['tags']])) API._log('DBG: %s' % sel_q) tags = await pg.fetch(sel_q) async with pg.transaction() as conn: # Delete current tags del_q = delete(ChannelTag).where( ChannelTag.channel_id == channel['id']) await conn.fetchrow(del_q) # Insert new ones for tag in tags: ins_q = insert(ChannelTag).values(channel_id=channel['id'], tag_id=tag['id']) await conn.fetchrow(ins_q) else: # if not changed, just get current ones sel_q = select([Tag]).select_from(outerjoin( ChannelTag, Tag)).where(ChannelTag.channel_id == channel['id']) tags = await pg.fetch(sel_q) channel['tags'] = [tag['name'] for tag in tags] if updated: upd_q = update(Channel).where(Channel.id == channel['id']).values( **updated) await pg.fetchrow(upd_q) channel.update(updated) # Return channel object await client.send_response({ 'data': channel, 'id': message['id'], 'action': message['action'] })
async def update_channel(client: ClientConnection, message: dict): # Getting general channel info response = await Telegram.send_telegram_request( bot_token=Telegram.get_bot_token(), method="getChat", params={"chat_id": message["username"]}) chat = response.get('result', None) if not chat: await client.send_error(message['id'], 404, "channel does not exist") return if chat['type'] != 'channel': await client.send_error(message['id'], 403, 'Not a channel, but %s' % chat['type']) return # Get channel members count chat["members"] = (await Telegram.send_telegram_request( bot_token=Telegram.get_bot_token(), method="getChatMembersCount", params={"chat_id": message["username"]}))["result"] # Get channel admin user list via special public bot admins = (await Telegram.send_telegram_request( bot_token=Telegram.get_admin_bot_token(), method="getChatAdministrators", params={"chat_id": message["username"]})) admins = admins.get('result', []) for admin in admins: # Bots are not welcome here if admin['user']['is_bot']: continue user_info = admin['user'] user_info['photo'] = await Telegram.get_user_profile_photo( bot_token=Telegram.get_bot_token(), user_id=user_info['id'], ) # Try to get channel photo if "photo" in chat: chat["photo"] = await Telegram.get_telegram_file( bot_token=Telegram.get_bot_token(), file_id=chat["photo"]["big_file_id"]) # Update DB.. sel_q = select([Channel]).where(Channel.telegram_id == chat["id"]) channel = await pg.fetchrow(sel_q) channel_dict = { 'telegram_id': chat["id"], 'username': "******" + chat["username"], 'title': chat["title"], 'photo': chat.get("photo", None), 'description': chat.get("description", None), 'members': chat["members"] } if channel is None: ins_q = insert(Channel).values(**channel_dict).returning( Channel.id) channel_id = await pg.fetchval(ins_q) else: channel_id = channel['id'] upd_q = update(Channel).where(Channel.id == channel_id).values( **channel_dict) await pg.fetchrow(upd_q) if admins: async with pg.transaction() as conn: if channel is not None: # Delete current admins for existing channel del_q = delete(ChannelAdmin).where( ChannelAdmin.channel_id == channel_id) await conn.fetchrow(del_q) # Populate admin list for admin in admins: user_info = admin.pop('user') sel_q = select([Client ]).where(Client.user_id == user_info['id']) db_client = await conn.fetchrow(sel_q) if db_client is None: ins_q = insert(Client).values( user_id=user_info["id"], first_name=user_info["first_name"], username=user_info.get("username", None), photo=user_info.get("photo", None)).returning(Client.id) admin_id = await conn.fetchval(ins_q) else: admin_id = db_client['id'] ins_q = insert(ChannelAdmin).values( channel_id=channel_id, admin_id=admin_id, owner=admin['status'] == 'creator', raw=admin) await conn.fetchrow(ins_q) API._log('%s channel "%s"' % ('Added' if channel is None else 'Updated', chat['username'])) await client.send_response({ 'id': message['id'], 'action': message['action'] })