def do_search_extracts(request): uri = request.GET.get('uri', None) if not uri: raise HTTPClientError("Please specify a URI") view_def = request.GET.get('view') or 'default' discussion = request.context user_id = authenticated_userid(request) if not user_id: # Straight from annotator token = request.headers.get('X-Annotator-Auth-Token') if token: token = decode_token(token, request.registry.settings['session.secret']) if token: user_id = token['userId'] user_id = user_id or Everyone if not user_has_permission(discussion.id, user_id, P_READ): raise HTTPForbidden() permissions = [P_READ] if not uri: raise HTTPBadRequest("Please specify a search uri") content = Webpage.get_by(url=uri) if content: extracts = Extract.default_db.query(Extract).filter_by( content=content).all() rows = [ extract.generic_json(view_def, user_id, permissions) for extract in extracts ] return {"total": len(extracts), "rows": rows} return {"total": 0, "rows": []}
def delete_extract(request): user_id = request.authenticated_userid discussion_id = int(request.matchdict['discussion_id']) if not user_id: # Straight from annotator token = request.headers.get('X-Annotator-Auth-Token') if token: token = decode_token(token, request.registry.settings['session.secret']) if token: user_id = token['userId'] user_id = user_id or Everyone extract_id = request.matchdict['id'] extract = Extract.get_instance(extract_id) if not (user_has_permission(discussion_id, user_id, P_EDIT_EXTRACT) or (user_has_permission(discussion_id, user_id, P_EDIT_MY_EXTRACT) and user_id == extract.owner_id)): raise HTTPForbidden() if not extract: return HTTPNoContent() # TODO: Tombstonable extracts??? extract.delete() return HTTPNoContent()
def delete_extract(request): user_id = authenticated_userid(request) discussion = request.context if not user_id: # Straight from annotator token = request.headers.get('X-Annotator-Auth-Token') if token: token = decode_token(token, request.registry.settings['session.secret']) if token: user_id = token['userId'] user_id = user_id or Everyone extract_id = request.matchdict['id'] extract = Extract.get_instance(extract_id) permissions = get_permissions(user_id, discussion.id, extract) if P_EDIT_EXTRACT not in permissions: raise HTTPForbidden() if not extract: return HTTPNoContent() # TODO: Tombstonable extracts??? extract.delete() return HTTPNoContent()
def on_message(self, msg): try: if getattr(self, 'socket', None): print("closing old socket") self.loop.add_callback(self.do_close) return if msg.startswith('discussion:') and self.valid: self.discussion = msg.split(':', 1)[1] if msg.startswith('token:') and self.valid: try: self.token = decode_token( msg.split(':', 1)[1], TOKEN_SECRET) self.userId = 'local:AgentProfile/' + str( self.token['userId']) except TokenInvalid: pass if self.token and self.discussion: # Check if token authorizes discussion r = requests.get( '%s/api/v1/discussion/%s/permissions/read/u/%s' % (SERVER_URL, self.discussion, self.token['userId'])) print(r.text) if r.text != 'true': return self.socket = context.socket(zmq.SUB) self.socket.connect(INTERNAL_SOCKET) self.socket.setsockopt(zmq.SUBSCRIBE, '*') self.socket.setsockopt(zmq.SUBSCRIBE, str(self.discussion)) self.loop = zmqstream.ZMQStream(self.socket, io_loop=io_loop) self.loop.on_recv(self.on_recv) print("connected") self.send('[{"@type":"Connection"}]') except Exception: capture_exception() self.do_close()
def delete_extract(request): user_id = authenticated_userid(request) discussion_id = int(request.matchdict['discussion_id']) if not user_id: # Straight from annotator token = request.headers.get('X-Annotator-Auth-Token') if token: token = decode_token( token, request.registry.settings['session.secret']) if token: user_id = token['userId'] user_id = user_id or Everyone extract_id = request.matchdict['id'] extract = Extract.get_instance(extract_id) if not (user_has_permission(discussion_id, user_id, P_EDIT_EXTRACT) or (user_has_permission(discussion_id, user_id, P_EDIT_MY_EXTRACT) and user_id == extract.owner_id)): raise HTTPForbidden() if not extract: return HTTPNoContent() with transaction.manager: # TODO: Tombstonable extracts??? Extract.default_db.delete(extract) request.response.status = HTTPNoContent.code return HTTPNoContent()
def on_message(self, msg): try: if getattr(self, "socket", None): print "closing old socket" self.loop.add_callback(self.do_close) return if msg.startswith("discussion:") and self.valid: self.discussion = msg.split(":", 1)[1] if msg.startswith("token:") and self.valid: try: self.token = decode_token(msg.split(":", 1)[1], TOKEN_SECRET) self.userId = "local:AgentProfile/" + str(self.token["userId"]) except TokenInvalid: pass if self.token and self.discussion: # Check if token authorizes discussion r = requests.get( "http://%s:%d/api/v1/discussion/%s/permissions/read/u/%s" % (SERVER_HOST, SERVER_PORT, self.discussion, self.token["userId"]) ) print r.text if r.text != "true": return self.socket = context.socket(zmq.SUB) self.socket.connect(INTERNAL_SOCKET) self.socket.setsockopt(zmq.SUBSCRIBE, "*") self.socket.setsockopt(zmq.SUBSCRIBE, str(self.discussion)) self.loop = zmqstream.ZMQStream(self.socket, io_loop=io_loop) self.loop.on_recv(self.on_recv) print "connected" self.send('[{"@type":"Connection"}]') except Exception: if raven_client: raven_client.captureException() raise
async def on_message(self, msg): try: if msg.startswith('discussion:') and self.valid: self.discussion = msg.split(':', 1)[1] log.debug('discussion_id: %s', self.discussion) if msg.startswith('token:') and self.valid: try: self.raw_token = msg.split(':', 1)[1] self.token = decode_token(self.raw_token, self.token_secret) if self.token['userId'] != Everyone: self.userId = 'local:Agent/' + str( self.token['userId']) else: self.userId = Everyone log.info('userId: %s', self.userId) except TokenInvalid: pass if self.token and self.discussion: # Check if token authorizes discussion async with self.http_client.get( '%s/api/v1/discussion/%s/permissions/Conversation.R/u/%s' % (self.server_url, self.discussion, self.token['userId']), headers={"Accept": "application/json"}) as resp: text = await resp.text() log.debug(text) if text != 'true': return log.info("connected") if self.userId == Everyone: self.roles = {Everyone} else: async with self.http_client.get( '%s/api/v1/discussion/%s/roles/allfor/%s' % (self.server_url, self.discussion, self.token['userId']), headers={"Accept": "application/json"}) as resp: text = await resp.text() self.roles = set(json.loads(text)) self.roles.add(Everyone) self.roles.add(Authenticated) self.roles.add('local:Agent/' + str(self.token['userId'])) self.task = self.loop.create_task(self.connect()) self.session.send('[{"@type":"Connection"}]') if self.token and self.raw_token and self.discussion and self.userId != Everyone: async with self.http_client.post( '%s/data/Discussion/%s/all_users/%d/connecting' % (self.server_url, self.discussion, self.token['userId']), data={'token': self.raw_token}) as resp: await resp.text() except Exception: capture_exception() await self.close()
def put_extract(request): """ Updating an Extract """ extract_id = request.matchdict['id'] user_id = authenticated_userid(request) discussion = request.context if not user_id: # Straight from annotator token = request.headers.get('X-Annotator-Auth-Token') if token: token = decode_token(token, request.registry.settings['session.secret']) if token: user_id = token['userId'] user_id = user_id or Everyone extract = Extract.get_instance(extract_id) if not extract: raise HTTPNotFound("Extract with id '%s' not found." % extract_id) permissions = get_permissions(user_id, discussion.id, extract) if P_EDIT_EXTRACT not in permissions: raise HTTPForbidden() updated_extract_data = json.loads(request.body) extract.owner_id = user_id or AgentProfile.get_database_id( extract.owner_id) extract.order = updated_extract_data.get('order', extract.order) extract.important = updated_extract_data.get('important', extract.important) idea_id = updated_extract_data.get('idIdea', None) if idea_id: idea = Idea.get_instance(idea_id) if (idea.discussion != extract.discussion): raise HTTPBadRequest( "Extract from discussion %s cannot be associated with an idea from a different discussion." % extract.get_discussion_id()) if not idea.has_permission_req(P_ASSOCIATE_EXTRACT): raise HTTPForbidden("Cannot associate extact with this idea") extract.idea = idea else: extract.idea = None Extract.default_db.add(extract) #TODO: Merge ranges. Sigh. return {'ok': True}
def put_extract(request): """ Updating an Extract """ extract_id = request.matchdict['id'] user_id = request.authenticated_userid discussion_id = int(request.matchdict['discussion_id']) if not user_id: # Straight from annotator token = request.headers.get('X-Annotator-Auth-Token') if token: token = decode_token(token, request.registry.settings['session.secret']) if token: user_id = token['userId'] user_id = user_id or Everyone updated_extract_data = json.loads(request.body) extract = Extract.get_instance(extract_id) if not extract: raise HTTPNotFound("Extract with id '%s' not found." % extract_id) if not (user_has_permission(discussion_id, user_id, P_EDIT_EXTRACT) or (user_has_permission(discussion_id, user_id, P_EDIT_MY_EXTRACT) and user_id == extract.owner_id)): return HTTPForbidden() extract.owner_id = user_id or get_database_id("User", extract.owner_id) extract.order = updated_extract_data.get('order', extract.order) extract.important = updated_extract_data.get('important', extract.important) idea_id = updated_extract_data.get('idIdea', None) if idea_id: idea = Idea.get_instance(idea_id) if (idea.discussion != extract.discussion): raise HTTPBadRequest( "Extract from discussion %s cannot be associated with an idea from a different discussion." % extract.get_discussion_id()) extract.idea = idea else: extract.idea = None Extract.default_db.add(extract) #TODO: Merge ranges. Sigh. return {'ok': True}
def put_extract(request): """ Updating an Extract """ extract_id = request.matchdict['id'] user_id = authenticated_userid(request) discussion_id = int(request.matchdict['discussion_id']) if not user_id: # Straight from annotator token = request.headers.get('X-Annotator-Auth-Token') if token: token = decode_token( token, request.registry.settings['session.secret']) if token: user_id = token['userId'] if not user_id: user_id = Everyone updated_extract_data = json.loads(request.body) extract = Extract.get_instance(extract_id) if not extract: raise HTTPNotFound("Extract with id '%s' not found." % extract_id) if not (user_has_permission(discussion_id, user_id, P_EDIT_EXTRACT) or (user_has_permission(discussion_id, user_id, P_EDIT_MY_EXTRACT) and user_id == extract.owner_id)): return HTTPForbidden() extract.owner_id = user_id or get_database_id("User", extract.owner_id) extract.order = updated_extract_data.get('order', extract.order) extract.important = updated_extract_data.get('important', extract.important) idea_id = updated_extract_data.get('idIdea', None) if idea_id: idea = Idea.get_instance(idea_id) if(idea.discussion != extract.discussion): raise HTTPBadRequest( "Extract from discussion %s cannot be associated with an idea from a different discussion." % extract.get_discussion_id()) extract.idea = idea else: extract.idea = None Extract.db.add(extract) #TODO: Merge ranges. Sigh. return {'ok': True}
def on_message(self, msg): try: if getattr(self, 'socket', None): log.info("closing old socket") self.loop.add_callback(self.do_close) return if msg.startswith('discussion:') and self.valid: self.discussion = msg.split(':', 1)[1] if msg.startswith('token:') and self.valid: try: self.raw_token = msg.split(':', 1)[1] self.token = decode_token(self.raw_token, TOKEN_SECRET) if self.token['userId'] != Everyone: self.userId = 'local:Agent/' + str( self.token['userId']) else: self.userId = Everyone except TokenInvalid: pass if self.token and self.discussion: # Check if token authorizes discussion r = requests.get( '%s/api/v1/discussion/%s/permissions/read/u/%s' % (SERVER_URL, self.discussion, self.token['userId']), headers={"Accept": "application/json"}) log.debug(r.text) if r.text != 'true': return self.socket = context.socket(zmq.SUB) self.socket.connect(INTERNAL_SOCKET) self.socket.setsockopt(zmq.SUBSCRIBE, b'*') self.socket.setsockopt( zmq.SUBSCRIBE, native_str_to_bytes(str(self.discussion))) self.loop = zmqstream.ZMQStream(self.socket, io_loop=io_loop) self.loop.on_recv(self.on_recv) log.info("connected") self.send('[{"@type":"Connection"}]') if self.token and self.raw_token and self.discussion and self.userId != Everyone: requests.post( '%s/data/Discussion/%s/all_users/%d/connecting' % (SERVER_URL, self.discussion, self.token['userId']), data={'token': self.raw_token}) except Exception: capture_exception() self.do_close()
def set_user_dis_connected(request, connecting): ctx = request.context discussion_id = ctx.get_discussion_id() if not discussion_id: # This view should only exist in discussion+user context raise HTTPNotFound() token = request.POST.get('token') # see if token corresponds to user user = ctx.get_instance_of_class(User) try: token = decode_token(token, TOKEN_SECRET) assert token['userId'] == user.id except TokenInvalid: raise HTTPUnauthorized() status = user.get_status_in_discussion(discussion_id) assert status if connecting: status.last_connected = datetime.now() else: status.last_disconnected = datetime.now() return HTTPOk()
def on_message(self, msg): try: if getattr(self, 'socket', None): print "closing old socket" self.loop.stop_on_recv() self.loop.close() self.socket = None self.loop = None if msg.startswith('discussion:') and self.valid: self.discussion = msg.split(':', 1)[1] if msg.startswith('token:') and self.valid: try: self.token = decode_token( msg.split(':', 1)[1], TOKEN_SECRET) except TokenInvalid: pass if self.token and self.discussion: # Check if token authorizes discussion r = requests.get( 'http://%s:%d/api/v1/discussion/%s/permissions/read/u/%s' % (SERVER_HOST, SERVER_PORT, self.discussion, self.token['userId'])) print r.text if r.text != 'true': return self.socket = context.socket(zmq.SUB) self.socket.connect(INTERNAL_SOCKET) self.socket.setsockopt(zmq.SUBSCRIBE, '*') self.socket.setsockopt(zmq.SUBSCRIBE, str(self.discussion)) self.loop = zmqstream.ZMQStream(self.socket, io_loop=io_loop) self.loop.on_recv(self.on_recv) print "connected" self.send('[{"@type":"Connection"}]') except Exception: if raven_client: raven_client.captureException() raise
def on_message(self, msg): try: if getattr(self, 'socket', None): print "closing old socket" self.loop.add_callback(self.do_close) return if msg.startswith('discussion:') and self.valid: self.discussion = msg.split(':', 1)[1] if msg.startswith('token:') and self.valid: try: self.token = decode_token( msg.split(':', 1)[1], TOKEN_SECRET) self.userId = 'local:AgentProfile/' + str( self.token['userId']) except TokenInvalid: pass if self.token and self.discussion: # Check if token authorizes discussion r = requests.get( '%s/api/v1/discussion/%s/permissions/read/u/%s' % (SERVER_URL, self.discussion, self.token['userId'])) print r.text if r.text != 'true': return self.socket = context.socket(zmq.SUB) self.socket.connect(INTERNAL_SOCKET) self.socket.setsockopt(zmq.SUBSCRIBE, '*') self.socket.setsockopt(zmq.SUBSCRIBE, str(self.discussion)) self.loop = zmqstream.ZMQStream(self.socket, io_loop=io_loop) self.loop.on_recv(self.on_recv) print "connected" self.send('[{"@type":"Connection"}]') except Exception: capture_exception() self.do_close()
def post_extract(request): """ Create a new extract. """ extract_data = json.loads(request.body) discussion = request.context db = discussion.db user_id = authenticated_userid(request) if not user_id: # Straight from annotator token = request.headers.get('X-Annotator-Auth-Token') if token: token = decode_token(token, request.registry.settings['session.secret']) if token: user_id = token['userId'] user_id = user_id or Everyone permissions = get_permissions(user_id, discussion_id) else: permissions = request.permissions if P_ADD_EXTRACT not in permissions: #TODO: maparent: restore this code once it works: #raise HTTPForbidden(result=ACLDenied(permission=P_ADD_EXTRACT)) raise HTTPForbidden() if not user_id or user_id == Everyone: # TODO: Create an anonymous user. raise HTTPServerError("Anonymous extracts are not implemeted yet.") content = None uri = extract_data.get('uri') important = extract_data.get('important', False) annotation_text = extract_data.get('text') target = extract_data.get('target') if not uri: # Extract from an internal post if not target: raise HTTPBadRequest("No target") target_class = sqla.get_named_class(target.get('@type')) if issubclass(target_class, Post): post_id = target.get('@id') post = Post.get_instance(post_id) if not post: raise HTTPNotFound("Post with id '%s' not found." % post_id) content = post elif issubclass(target_class, Webpage): uri = target.get('url') if uri and not content: content = Webpage.get_instance(uri) if not content: # TODO: maparent: This is actually a singleton pattern, should be # handled by the AnnotatorSource now that it exists... source = db.query(AnnotatorSource).filter_by( discussion=discussion).filter( cast(AnnotatorSource.name, Unicode) == 'Annotator').first() if not source: source = AnnotatorSource(name='Annotator', discussion=discussion) db.add(source) content = Webpage(url=uri, discussion=discussion) db.add(content) extract_body = extract_data.get('quote', None) idea_id = extract_data.get('idIdea', None) if idea_id: idea = Idea.get_instance(idea_id) if (idea.discussion.id != discussion.id): raise HTTPBadRequest( "Extract from discussion %s cannot be associated with an idea from a different discussion." % extract.get_discussion_id()) if not idea.has_permission_req(P_ASSOCIATE_EXTRACT): raise HTTPForbidden("Cannot associate extact with this idea") else: idea = None new_extract = Extract(creator_id=user_id, owner_id=user_id, discussion=discussion, idea=idea, important=important, annotation_text=annotation_text, content=content) db.add(new_extract) for range_data in extract_data.get('ranges', []): range = TextFragmentIdentifier(extract=new_extract, body=extract_body, xpath_start=range_data['start'], offset_start=range_data['startOffset'], xpath_end=range_data['end'], offset_end=range_data['endOffset']) db.add(range) db.flush() return {'ok': True, '@id': new_extract.uri()}
def post_extract(request): """ Create a new extract. """ extract_data = json.loads(request.body) discussion_id = int(request.matchdict['discussion_id']) user_id = authenticated_userid(request) if not user_id: # Straight from annotator token = request.headers.get('X-Annotator-Auth-Token') if token: token = decode_token( token, request.registry.settings['session.secret']) if token: user_id = token['userId'] user_id = user_id or Everyone if not user_has_permission(discussion_id, user_id, P_ADD_EXTRACT): #TODO: maparent: restore this code once it works: #return HTTPForbidden(result=ACLDenied(permission=P_ADD_EXTRACT)) return HTTPForbidden() if not user_id or user_id == Everyone: # TODO: Create an anonymous user. raise HTTPServerError("Anonymous extracts are not implemeted yet.") content = None uri = extract_data.get('uri') important = extract_data.get('important', False) annotation_text = None if uri: # Straight from annotator annotation_text = extract_data.get('text') else: target = extract_data.get('target') if not (target or uri): raise HTTPBadRequest("No target") target_class = sqla.get_named_class(target.get('@type')) if issubclass(target_class, Post): post_id = target.get('@id') post = Post.get_instance(post_id) if not post: raise HTTPNotFound( "Post with id '%s' not found." % post_id) content = post elif issubclass(target_class, Webpage): uri = target.get('url') if uri and not content: content = Webpage.get_instance(uri) if not content: # TODO: maparent: This is actually a singleton pattern, should be # handled by the AnnotatorSource now that it exists... source = AnnotatorSource.default_db.query(AnnotatorSource).filter_by( discussion_id=discussion_id).filter( cast(AnnotatorSource.name, Unicode) == 'Annotator').first() if not source: source = AnnotatorSource( name='Annotator', discussion_id=discussion_id, type='source') content = Webpage(url=uri, discussion_id=discussion_id) extract_body = extract_data.get('quote', '') idea_id = extract_data.get('idIdea', None) if idea_id: idea = Idea.get_instance(idea_id) if(idea.discussion.id != discussion_id): raise HTTPBadRequest( "Extract from discussion %s cannot be associated with an idea from a different discussion." % extract.get_discussion_id()) else: idea = None new_extract = Extract( creator_id=user_id, owner_id=user_id, discussion_id=discussion_id, body=extract_body, idea=idea, important=important, annotation_text=annotation_text, content=content ) Extract.default_db.add(new_extract) for range_data in extract_data.get('ranges', []): range = TextFragmentIdentifier( extract=new_extract, xpath_start=range_data['start'], offset_start=range_data['startOffset'], xpath_end=range_data['end'], offset_end=range_data['endOffset']) TextFragmentIdentifier.default_db.add(range) Extract.default_db.flush() return {'ok': True, '@id': new_extract.uri()}