Example #1
0
File: api.py Project: nshkuro/h
def create_db():
    """Create the ElasticSearch index for Annotations and Documents"""
    try:
        es.conn.indices.create(es.index)
    except elasticsearch_exceptions.RequestError as e:
        if not (e.error.startswith('IndexAlreadyExistsException')
                or e.error.startswith('InvalidIndexNameException')):
            raise
    except elasticsearch_exceptions.ConnectionError as e:
        msg = ('Can not access ElasticSearch at {0}! '
               'Check to ensure it is running.').format(es.host)
        raise elasticsearch_exceptions.ConnectionError('N/A', msg, e)
    # Pylint issue #258: https://bitbucket.org/logilab/pylint/issue/258
    #
    # pylint: disable=unexpected-keyword-arg
    es.conn.cluster.health(wait_for_status='yellow')
    # pylint: enable=unexpected-keyword-arg

    try:
        Annotation.update_settings()
        Annotation.create_all()
        Document.create_all()
    except elasticsearch_exceptions.RequestError as e:
        if e.error.startswith('MergeMappingException'):
            date = time.strftime('%Y-%m-%d')
            message = ("Elasticsearch index mapping is incorrect! Please "
                       "reindex it. For example, run: "
                       "./bin/hypothesis reindex {0} {1} {1}-{2}"
                       .format('yourconfig.ini', es.index, date)
                       )
            log.critical(message)
            raise RuntimeError(message)
        raise
Example #2
0
    def session(self, db_session, added_ann_id, changed_ann_id,
                deleted_ann_id):
        # Populate the DB session with different types of change relative to the
        # last-committed state. We could use any model object for this purpose
        # but annotations are the primary object in the system.

        doc = Document(web_uri="https://example.org")
        changed = Annotation(id=changed_ann_id,
                             userid="foo",
                             groupid="wibble",
                             document=doc)
        deleted = Annotation(id=deleted_ann_id,
                             userid="foo",
                             groupid="wibble",
                             document=doc)
        db_session.add(changed)
        db_session.add(deleted)
        db_session.commit()

        changed.text = "changed text"
        db_session.delete(deleted)

        added = Annotation(id=added_ann_id,
                           userid="foo",
                           groupid="wibble",
                           document=doc)
        db_session.add(added)

        return db_session
Example #3
0
def test_generate_notifications_only_if_author_can_read_reply(Subscriptions,
                                                              render_reply_notification,
                                                              effective_principals):
    """
    If the annotation is not readable by the parent author, no notifications
    should be generated.
    """
    private_annotation = Annotation.fetch(6)
    shared_annotation = Annotation.fetch(7)
    request = _create_request()
    effective_principals.return_value = [
        security.Everyone,
        security.Authenticated,
        'acct:[email protected]',
        'group:wibble',
    ]
    Subscriptions.get_active_subscriptions_for_a_type.return_value = [
        MockSubscription(id=1, uri='acct:[email protected]')
    ]
    render_reply_notification.return_value = (
        'Dummy subject',
        'Dummy text',
        'Dummy HTML',
        ['*****@*****.**']
    )

    notifications = rt.generate_notifications(request, private_annotation, 'create')
    assert list(notifications) == []

    notifications = rt.generate_notifications(request, shared_annotation, 'create')
    assert list(notifications) != []
Example #4
0
def parent_values(annotation):
    if 'references' in annotation:
        parent = Annotation.fetch(annotation['references'][-1])
        if 'references' in parent:
            grandparent = Annotation.fetch(parent['references'][-1])
            parent['quote'] = grandparent['text']
        return parent
    else:
        return {}
Example #5
0
def parent_values(annotation):
    if 'references' in annotation:
        parent = Annotation.fetch(annotation['references'][-1])
        if 'references' in parent:
            grandparent = Annotation.fetch(parent['references'][-1])
            parent['quote'] = grandparent['text']
        return parent
    else:
        return {}
Example #6
0
File: api.py Project: Forethinker/h
def create_db():
    """Create the ElasticSearch index for Annotations and Documents"""
    try:
        es.conn.indices.create(es.index)
    except elasticsearch_exceptions.RequestError as e:
        if not (e.error.startswith('IndexAlreadyExistsException')
                or e.error.startswith('InvalidIndexNameException')):
            raise
    except elasticsearch_exceptions.ConnectionError as e:
        msg = ('Can not access ElasticSearch at {0}! '
               'Check to ensure it is running.').format(es.host)
        raise elasticsearch_exceptions.ConnectionError('N/A', msg, e)
    es.conn.cluster.health(wait_for_status='yellow')
    Annotation.update_settings()
    Annotation.create_all()
    Document.create_all()
Example #7
0
 def reply(self, annotations, parent):
     # We need to create a document object to provide the title, and
     # ensure it is associated with the annotation through the
     # annotation's `target_uri`
     doc = Document.find_or_create_by_uris(db.Session,
                                           claimant_uri='http://example.net/foo',
                                           uris=[]).one()
     doc.meta.append(DocumentMeta(type='title',
                                  value=['Some document'],
                                  claimant='http://example.com/foo'))
     reply = Annotation(**FIXTURE_DATA['reply'])
     reply.target_uri = 'http://example.net/foo'
     reply.references = [parent.id]
     db.Session.add(reply)
     db.Session.flush()
     annotations[reply.id] = reply
     return reply
Example #8
0
 def reply(self):
     common = {
         "id": "bar456",
         "created": datetime.datetime.utcnow(),
         "updated": datetime.datetime.utcnow(),
         "text": "No it is not!",
     }
     return Annotation(target_uri="http://example.org/", **common)
Example #9
0
 def reply(self, annotations, db_session, parent):
     # We need to create a document object to provide the title, and
     # ensure it is associated with the annotation through the
     # annotation's `target_uri`
     doc = Document.find_or_create_by_uris(db_session,
                                           claimant_uri='http://example.net/foo',
                                           uris=[]).one()
     doc.meta.append(DocumentMeta(type='title',
                                  value=['Some document'],
                                  claimant='http://example.com/foo'))
     reply = Annotation(**FIXTURE_DATA['reply'])
     reply.target_uri = 'http://example.net/foo'
     reply.references = [parent.id]
     db_session.add(reply)
     db_session.flush()
     annotations[reply.id] = reply
     return reply
Example #10
0
def test_generate_notifications_empty_if_action_not_create():
    """If the action is not 'create', no notifications should be generated."""
    annotation = Annotation()
    request = DummyRequest()

    notifications = rt.generate_notifications(request, annotation, 'update')

    assert list(notifications) == []
Example #11
0
def annotation(db_session):
    from h.models import Annotation
    ann = Annotation(userid='acct:testuser@localhost',
                     groupid='__world__',
                     shared=True)
    db_session.add(ann)
    db_session.commit()
    return ann
Example #12
0
 def parent(self):
     common = {
         "id": "foo123",
         "created": datetime.datetime.utcnow(),
         "updated": datetime.datetime.utcnow(),
         "text": "Foo is true",
     }
     return Annotation(target_uri="http://example.org/", **common)
Example #13
0
def annotations_index(context, request):
    """Do a search for all annotations on anything and return results.

    This will use the default limit, 20 at time of writing, and results
    are ordered most recent first.
    """
    user = get_user(request)
    return Annotation.search(user=user)
Example #14
0
def test_generate_notifications_empty_if_annotation_has_no_parent():
    """If the annotation has no parent no notifications should be generated."""
    annotation = Annotation.fetch(0)
    request = DummyRequest()

    notifications = rt.generate_notifications(request, annotation, 'create')

    assert list(notifications) == []
Example #15
0
 def parent(self):
     common = {
         'id': 'foo123',
         'created': datetime.datetime.utcnow(),
         'updated': datetime.datetime.utcnow(),
         'text': 'Foo is true',
     }
     return Annotation(target_uri='http://example.org/', **common)
Example #16
0
    def __getitem__(self, key):
        annotation = Annotation.fetch(key)
        if annotation is None:
            raise KeyError(key)
        annotation.__name__ = key
        annotation.__parent__ = self

        return annotation
Example #17
0
def annotations_index(context, request):
    """Do a search for all annotations on anything and return results.

    This will use the default limit, 20 at time of writing, and results
    are ordered most recent first.
    """
    user = get_user(request)
    return Annotation.search(user=user)
Example #18
0
 def reply(self):
     common = {
         'id': 'bar456',
         'created': datetime.datetime.utcnow(),
         'updated': datetime.datetime.utcnow(),
         'text': 'No it is not!',
     }
     return Annotation(target_uri='http://example.org/', **common)
Example #19
0
    def send_annotations(self):
        request = self.request
        user = get_user(request)
        annotations = Annotation.search_raw(query=self.query.query, user=user)
        self.received = len(annotations)

        packet = _annotation_packet(annotations, 'past')
        data = json.dumps(packet)
        self.send(data)
Example #20
0
    def session(self, db_session, added_ann_id, changed_ann_id, deleted_ann_id):
        # Populate the DB session with different types of change relative to the
        # last-committed state. We could use any model object for this purpose
        # but annotations are the primary object in the system.

        doc = Document(web_uri='https://example.org')
        changed = Annotation(id=changed_ann_id, userid='foo', groupid='wibble', document=doc)
        deleted = Annotation(id=deleted_ann_id, userid='foo', groupid='wibble', document=doc)
        db_session.add(changed)
        db_session.add(deleted)
        db_session.commit()

        changed.text = 'changed text'
        db_session.delete(deleted)

        added = Annotation(id=added_ann_id, userid='foo', groupid='wibble', document=doc)
        db_session.add(added)

        return db_session
Example #21
0
def test_og_no_document(pyramid_request, group_service, links_service, sidebar_app):
    annotation = Annotation(id='123', userid='foo', target_uri='http://example.com')
    context = AnnotationResource(annotation, group_service, links_service)
    sidebar_app.side_effect = _fake_sidebar_app

    ctx = main.annotation_page(context, pyramid_request)

    def test(d):
        return 'foo' in d['content']
    assert any(test(d) for d in ctx['meta_attrs'])
def group_with_two_users(db_session, factories):
    """
    Create a group with two members and an annotation created by each.
    """
    creator = factories.User()
    member = factories.User()

    group = Group(authority=creator.authority, creator=creator, members=[creator, member],
                  name='test', pubid='group_with_two_users')
    db_session.add(group)

    doc = Document(web_uri='https://example.org')
    creator_ann = Annotation(userid=creator.userid, groupid=group.pubid, document=doc)
    member_ann = Annotation(userid=member.userid, groupid=group.pubid, document=doc)

    db_session.add(creator_ann)
    db_session.add(member_ann)
    db_session.flush()

    return (group, creator, member, creator_ann, member_ann)
Example #23
0
def search(context, request):
    """Search the database for annotations matching with the given query."""

    # The search results are filtered for the authenticated user
    user = get_user(request)

    # Compile search parameters
    search_params = _search_params(request.params, user=user)

    log.debug("Searching with user=%s, for uri=%s",
              user.id if user else 'None',
              search_params.get('uri'))

    results = Annotation.search(**search_params)
    total = Annotation.count(**search_params)

    return {
        'rows': results,
        'total': total,
    }
Example #24
0
def _create_annotation(fields, user):
    """Create and store an annotation"""

    # Some fields are not to be set by the user, ignore them
    for field in PROTECTED_FIELDS:
        fields.pop(field, None)

    # Create Annotation instance
    annotation = Annotation(fields)

    annotation['user'] = user.id
    annotation['consumer'] = user.consumer.key

    # Save it in the database
    annotation.save()

    log.debug('Created annotation; user: %s, consumer key: %s',
              annotation['user'], annotation['consumer'])

    return annotation
Example #25
0
def create_db():
    """Create the ElasticSearch index for Annotations and Documents"""
    try:
        es.conn.indices.create(es.index)
    except elasticsearch_exceptions.RequestError as e:
        if not (e.error.startswith('IndexAlreadyExistsException')
                or e.error.startswith('InvalidIndexNameException')):
            raise
    except elasticsearch_exceptions.ConnectionError as e:
        msg = ('Can not access ElasticSearch at {0}! '
               'Check to ensure it is running.').format(es.host)
        raise elasticsearch_exceptions.ConnectionError('N/A', msg, e)
    # Pylint issue #258: https://bitbucket.org/logilab/pylint/issue/258
    #
    # pylint: disable=unexpected-keyword-arg
    es.conn.cluster.health(wait_for_status='yellow')
    # pylint: enable=unexpected-keyword-arg
    Annotation.update_settings()
    Annotation.create_all()
    Document.create_all()
Example #26
0
def _create_annotation(fields, user):
    """Create and store an annotation"""

    # Some fields are not to be set by the user, ignore them
    for field in PROTECTED_FIELDS:
        fields.pop(field, None)

    # Create Annotation instance
    annotation = Annotation(fields)

    annotation['user'] = user.id
    annotation['consumer'] = user.consumer.key

    # Save it in the database
    annotation.save()

    log.debug('Created annotation; user: %s, consumer key: %s',
              annotation['user'], annotation['consumer'])

    return annotation
Example #27
0
def test_og_no_document(
    pyramid_request, groupfinder_service, links_service, sidebar_app
):
    annotation = Annotation(id="123", userid="foo", target_uri="http://example.com")
    context = AnnotationContext(annotation, groupfinder_service, links_service)
    sidebar_app.side_effect = _fake_sidebar_app

    ctx = main.annotation_page(context, pyramid_request)

    expected = Any.dict.containing({"content": Any.string.containing("foo")})

    assert expected in ctx["meta_attrs"]
Example #28
0
 def parent(self, storage_driver):
     common = {
         'id': 'foo123',
         'created': datetime.datetime.utcnow(),
         'updated': datetime.datetime.utcnow(),
         'text': 'Foo is true',
     }
     uri = 'http://example.org/'
     if storage_driver == 'elastic':
         return elastic.Annotation(uri=uri, **common)
     else:
         return Annotation(target_uri=uri, **common)
Example #29
0
 def reply(self, storage_driver):
     common = {
         'id': 'bar456',
         'created': datetime.datetime.utcnow(),
         'updated': datetime.datetime.utcnow(),
         'text': 'No it is not!',
     }
     uri = 'http://example.org/'
     if storage_driver == 'elastic':
         return elastic.Annotation(uri=uri, **common)
     else:
         return Annotation(target_uri=uri, **common)
Example #30
0
def test_generate_notifications_checks_subscriptions(Subscriptions):
    """If the annotation has a parent, then proceed to check subscriptions."""
    request = _create_request()
    annotation = Annotation.fetch(1)
    Subscriptions.get_active_subscriptions_for_a_type.return_value = []

    notifications = rt.generate_notifications(request, annotation, "create")

    # Read the generator
    list(notifications)

    Subscriptions.get_active_subscriptions_for_a_type.assert_called_with(REPLY_TYPE)
Example #31
0
def handle_annotation_event(message, socket):
    """
    Get message about annotation event `message` to be sent to `socket`.

    Inspects the embedded annotation event and decides whether or not the
    passed socket should receive notification of the event.

    Returns None if the socket should not receive any message about this
    annotation event, otherwise a dict containing information about the event.
    """
    action = message['action']
    annotation = Annotation(**message['annotation'])

    if action == 'read':
        return None

    if message['src_client_id'] == socket.client_id:
        return None

    if annotation.get('nipsa') and (socket.request.authenticated_userid !=
                                    annotation.get('user', '')):
        return None

    if not _authorized_to_read(socket.request, annotation):
        return None

    # We don't send anything until we have received a filter from the client
    if socket.filter is None:
        return None

    if not socket.filter.match(annotation, action):
        return None

    return {
        'payload': [annotation],
        'type': 'annotation-notification',
        'options': {
            'action': action
        },
    }
Example #32
0
 def reply(self, annotations, storage_driver, parent):
     if storage_driver == 'elastic':
         reply = elastic.Annotation(**FIXTURE_DATA['reply_elastic'])
         reply['document'] = {'title': ['Some document']}
         reply['references'] = [parent.id]
     else:
         # We need to create a document object to provide the title, and
         # ensure it is associated with the annotation through the
         # annotation's `target_uri`
         doc = Document.find_or_create_by_uris(db.Session,
                                               claimant_uri='http://example.net/foo',
                                               uris=[]).one()
         doc.meta.append(DocumentMeta(type='title',
                                      value=['Some document'],
                                      claimant='http://example.com/foo'))
         reply = Annotation(**FIXTURE_DATA['reply_postgres'])
         reply.target_uri = 'http://example.net/foo'
         reply.references = [parent.id]
         db.Session.add(reply)
         db.Session.flush()
     annotations[reply.id] = reply
     return reply
Example #33
0
def test_generate_notifications_checks_subscriptions(Subscriptions):
    """If the annotation has a parent, then proceed to check subscriptions."""
    request = _create_request()
    annotation = Annotation.fetch(1)
    Subscriptions.get_active_subscriptions_for_a_type.return_value = []

    notifications = rt.generate_notifications(request, annotation, 'create')

    # Read the generator
    list(notifications)

    Subscriptions.get_active_subscriptions_for_a_type.assert_called_with(
        REPLY_TYPE)
Example #34
0
File: streamer.py Project: chrber/h
def handle_annotation_event(message, socket):
    """
    Get message about annotation event `message` to be sent to `socket`.

    Inspects the embedded annotation event and decides whether or not the
    passed socket should receive notification of the event.

    Returns None if the socket should not receive any message about this
    annotation event, otherwise a dict containing information about the event.
    """
    action = message['action']
    annotation = Annotation(**message['annotation'])

    if action == 'read':
        return None

    if message['src_client_id'] == socket.client_id:
        return None

    if annotation.get('nipsa') and (
            socket.request.authenticated_userid != annotation.get('user', '')):
        return None

    if not _authorized_to_read(socket.request, annotation):
        return None

    # We don't send anything until we have received a filter from the client
    if socket.filter is None:
        return None

    if not socket.filter.match(annotation, action):
        return None

    return {
        'payload': [annotation],
        'type': 'annotation-notification',
        'options': {'action': action},
    }
Example #35
0
def broadcast_from_queue(queue, sockets):
    """
    Pulls messages from a passed queue object, and handles dispatching them to
    appropriate active sessions.
    """
    for message in queue:
        data_in = json.loads(message.body)
        action = data_in['action']
        annotation = Annotation(**data_in['annotation'])
        payload = _annotation_packet([annotation], action)
        data_out = json.dumps(payload)
        for socket in list(sockets):
            if should_send_event(socket, annotation, data_in):
                socket.send(data_out)
Example #36
0
def test_og_no_document(pyramid_request, group_service, links_service,
                        sidebar_app):
    annotation = Annotation(id="123",
                            userid="foo",
                            target_uri="http://example.com")
    context = AnnotationContext(annotation, group_service, links_service)
    sidebar_app.side_effect = _fake_sidebar_app

    ctx = main.annotation_page(context, pyramid_request)

    def test(d):
        return "foo" in d["content"]

    assert any(test(d) for d in ctx["meta_attrs"])
Example #37
0
def _search(request_params, user=None):
    # Compile search parameters
    search_params = _search_params(request_params, user=user)

    log.debug("Searching with user=%s, for uri=%s",
              user.id if user else 'None', request_params.get('uri'))

    if 'any' in search_params['query']:
        # Handle any field parameters
        query = _add_any_field_params_into_query(search_params)
        results = Annotation.search_raw(query)

        params = {'search_type': 'count'}
        count = Annotation.search_raw(query, params, raw_result=True)
        total = count['hits']['total']
    else:
        results = Annotation.search(**search_params)
        total = Annotation.count(**search_params)

    return {
        'rows': results,
        'total': total,
    }
Example #38
0
def test_check_conditions_false_stops_sending():
    """If the check conditions() returns False, no notifications are generated"""
    request = _create_request()

    annotation = Annotation.fetch(1)
    with patch('h.notification.reply_template.Subscriptions') as mock_subs:
        mock_subs.get_active_subscriptions_for_a_type.return_value = [
            MockSubscription(id=1, uri='acct:[email protected]')
        ]
        with patch('h.notification.reply_template.check_conditions') as mock_conditions:
            mock_conditions.return_value = False
            with pytest.raises(StopIteration):
                msgs = rt.generate_notifications(request, annotation, 'create')
                msgs.next()
Example #39
0
    def send_annotations(self):
        request = self.request
        user = get_user(request)
        annotations = Annotation.search_raw(query=self.query.query, user=user)
        self.received = len(annotations)

        # Can send zero to indicate that no past data is matched
        packet = {
            'payload': annotations,
            'type': 'annotation-notification',
            'options': {
                'action': 'past',
            }
        }
        self.send(packet)
Example #40
0
def test_send_if_everything_is_okay():
    """Test whether we generate notifications if every condition is okay"""
    request = _create_request()

    annotation = Annotation.fetch(1)
    with patch('h.notification.reply_template.Subscriptions') as mock_subs:
        mock_subs.get_active_subscriptions_for_a_type.return_value = [
            MockSubscription(id=1, uri='acct:[email protected]')
        ]
        with patch('h.notification.reply_template.check_conditions') as mock_conditions:
            mock_conditions.return_value = True
            with patch('h.notification.reply_template.render') as mock_render:
                mock_render.return_value = ''
                with patch('h.notification.reply_template.get_user_by_name') as mock_user_db:
                    user = Mock()
                    user.email = '*****@*****.**'
                    mock_user_db.return_value = user
                    msgs = rt.generate_notifications(request, annotation, 'create')
                    msgs.next()
Example #41
0
def _add_any_field_params_into_query(search_params):
    """Add any_field parameters to ES query"""
    any_terms = search_params['query'].getall('any')
    del search_params['query']['any']

    offset = search_params.get('offset', None)
    limit = search_params.get('limit', None)
    query = Annotation._build_query(search_params['query'], offset, limit)

    multi_match_query = {
        'multi_match': {
            'query': any_terms,
            'type': 'cross_fields',
            'fields': ['quote', 'tags', 'text', 'uri', 'user']
        }
    }

    # Remove match_all if we add the multi-match part
    if 'match_all' in query['query']['bool']['must'][0]:
        query['query']['bool']['must'] = []
    query['query']['bool']['must'].append(multi_match_query)

    return query
Example #42
0
def parent_values(annotation):
    if 'references' in annotation:
        parent = Annotation.fetch(annotation['references'][-1])
        return parent
    else:
        return {}
Example #43
0
def delete_db():
    Annotation.drop_all()
    Document.drop_all()
Example #44
0
 def parent(self, annotations):
     parent = Annotation(**FIXTURE_DATA['parent'])
     annotations[parent.id] = parent
     return parent
Example #45
0
def delete_db():
    Annotation.drop_all()
    Document.drop_all()