Beispiel #1
0
def _get_ideas_real(discussion, view_def=None, ids=None, user_id=None):
    user_id = user_id or Everyone
    # optimization: Recursive widget links.
    from assembl.models import (
        Widget, IdeaWidgetLink, IdeaDescendantsShowingWidgetLink)
    universal_widget_links = []
    by_idea_widget_links = defaultdict(list)
    widget_links = discussion.db.query(IdeaWidgetLink
        ).join(Widget).join(Discussion).filter(
        Widget.test_active(), Discussion.id == discussion.id,
        IdeaDescendantsShowingWidgetLink.polymorphic_filter()
        ).options(joinedload_all(IdeaWidgetLink.idea)).all()
    for wlink in widget_links:
        if isinstance(wlink.idea, RootIdea):
            universal_widget_links.append({
                '@type': wlink.external_typename(),
                'widget': Widget.uri_generic(wlink.widget_id)})
        else:
            for id in wlink.idea.get_all_descendants(True):
                by_idea_widget_links[Idea.uri_generic(id)].append({
                    '@type': wlink.external_typename(),
                    'widget': Widget.uri_generic(wlink.widget_id)})

    next_synthesis = discussion.get_next_synthesis()
    ideas = discussion.db.query(Idea).filter_by(
        discussion_id=discussion.id
    )

    ideas = ideas.outerjoin(SubGraphIdeaAssociation,
                    and_(SubGraphIdeaAssociation.sub_graph_id==next_synthesis.id, SubGraphIdeaAssociation.idea_id==Idea.id)
        )
    
    ideas = ideas.outerjoin(IdeaLink,
                    and_(IdeaLink.target_id==Idea.id)
        )
    
    ideas = ideas.order_by(IdeaLink.order, Idea.creation_date)
    
    if ids:
        ids = [get_database_id("Idea", id) for id in ids]
        ideas = ideas.filter(Idea.id.in_(ids))
    # remove tombstones
    ideas = ideas.filter(and_(*Idea.base_conditions()))
    ideas = ideas.options(
        joinedload_all(Idea.source_links),
        joinedload_all(Idea.has_showing_widget_links),
        undefer(Idea.num_children))

    permissions = get_permissions(user_id, discussion.id)
    Idea.prepare_counters(discussion.id, True)
    retval = [idea.generic_json(view_def, user_id, permissions)
              for idea in ideas]
    retval = [x for x in retval if x is not None]
    for r in retval:
        if r.get('widget_links', None) is not None:
            links = r['widget_links'][:]
            links.extend(universal_widget_links)
            links.extend(by_idea_widget_links[r['@id']])
            r['active_widget_links'] = links
    return retval
Beispiel #2
0
def _get_ideas_real(discussion, view_def=None, ids=None, user_id=None):
    user_id = user_id or Everyone
    # optimization: Recursive widget links.
    from assembl.models import (
        Widget, IdeaWidgetLink, IdeaDescendantsShowingWidgetLink)
    universal_widget_links = []
    by_idea_widget_links = defaultdict(list)
    widget_links = discussion.db.query(IdeaWidgetLink
        ).join(Widget).join(Discussion).filter(
        Widget.test_active(), Discussion.id == discussion.id,
        IdeaDescendantsShowingWidgetLink.polymorphic_filter()
        ).options(joinedload_all(IdeaWidgetLink.idea)).all()
    for wlink in widget_links:
        if isinstance(wlink.idea, RootIdea):
            universal_widget_links.append({
                '@type': wlink.external_typename(),
                'widget': Widget.uri_generic(wlink.widget_id)})
        else:
            for id in wlink.idea.get_all_descendants(True):
                by_idea_widget_links[Idea.uri_generic(id)].append({
                    '@type': wlink.external_typename(),
                    'widget': Widget.uri_generic(wlink.widget_id)})

    next_synthesis = discussion.get_next_synthesis()
    ideas = discussion.db.query(Idea).filter_by(
        discussion_id=discussion.id
    )

    ideas = ideas.outerjoin(SubGraphIdeaAssociation,
                    and_(SubGraphIdeaAssociation.sub_graph_id==next_synthesis.id, SubGraphIdeaAssociation.idea_id==Idea.id)
        )
    
    ideas = ideas.outerjoin(IdeaLink,
                    and_(IdeaLink.target_id==Idea.id)
        )
    
    ideas = ideas.order_by(IdeaLink.order, Idea.creation_date)
    
    if ids:
        ids = [get_database_id("Idea", id) for id in ids]
        ideas = ideas.filter(Idea.id.in_(ids))
    # remove tombstones
    ideas = ideas.filter(and_(*Idea.base_conditions()))
    ideas = ideas.options(
        joinedload_all(Idea.source_links),
        joinedload_all(Idea.has_showing_widget_links),
        undefer(Idea.num_children))

    permissions = get_permissions(user_id, discussion.id)
    Idea.prepare_counters(discussion.id, True)
    retval = [idea.generic_json(view_def, user_id, permissions)
              for idea in ideas]
    retval = [x for x in retval if x is not None]
    for r in retval:
        if r.get('widget_links', None) is not None:
            links = r['widget_links'][:]
            links.extend(universal_widget_links)
            links.extend(by_idea_widget_links[r['@id']])
            r['active_widget_links'] = links
    return retval
Beispiel #3
0
def DISABLEDtest_voting_widget_criteria(discussion, test_app, subidea_1_1,
                                        criterion_1, criterion_2, criterion_3,
                                        admin_user, participant1_user,
                                        test_session):
    # Post the initial configuration
    db = discussion.db
    criteria = (criterion_1, criterion_2)
    criteria_def = [{
        "@id": criterion.uri(),
        "short_title": criterion.short_title
    } for criterion in criteria]
    new_widget_loc = test_app.post(
        '/data/Discussion/%d/widgets' % (discussion.id, ), {
            'type': 'MultiCriterionVotingWidget',
            'settings': json.dumps({"criteria": criteria_def})
        })
    assert new_widget_loc.status_code == 201
    # Get the widget from the db
    db.flush()
    new_widget = Widget.get_instance(new_widget_loc.location)
    assert new_widget
    db.expire(new_widget, ('criteria', ))
    # Get the widget from the api
    widget_rep = test_app.get(local_to_absolute(new_widget.uri()),
                              {'target': subidea_1_1.uri()},
                              headers={"Accept": "application/json"})
    assert widget_rep.status_code == 200
    widget_rep = widget_rep.json
    voting_urls = widget_rep['voting_urls']
    assert voting_urls
    assert widget_rep['criteria']
    assert widget_rep['criteria_url']

    # Note: At this point, we have two copies of the criteria in the rep.
    # One is the full ideas in widget_rep['criteria'], the other is
    # as specified originally in widget_rep['settings']['criteria'].
    # In what follows I'll use the former.

    # The criteria should also be in the criteria url
    criteria_url = local_to_absolute(widget_rep['criteria_url'])
    test = test_app.get(criteria_url)
    assert test.status_code == 200
    assert len(test.json) == 2
    assert {x['@id'] for x in test.json} == {c.uri() for c in criteria}
    assert test.json == widget_rep['criteria']

    # Set a new set of criteria
    criteria = (criterion_2, criterion_3)
    criteria_def = [{
        "@id": criterion.uri(),
        "short_title": criterion.short_title
    } for criterion in criteria]
    test_app.put(criteria_url, json.dumps(criteria_def), headers=JSON_HEADER)
    db.flush()
    db.expire(new_widget, ('criteria', ))

    # Get them back
    test = test_app.get(criteria_url)
    assert test.status_code == 200
    assert len(test.json) == 2
    assert {x['@id'] for x in test.json} == {c.uri() for c in criteria}
Beispiel #4
0
def test_voting_widget(discussion, test_app, subidea_1_1, criterion_1,
                       criterion_2, criterion_3, admin_user, participant1_user,
                       test_session, request):
    # Post the initial configuration
    db = discussion.db
    criteria = (criterion_1, criterion_2, criterion_3)
    new_widget_loc = test_app.post(
        '/data/Discussion/%d/widgets' % (discussion.id, ), {
            'type': 'MultiCriterionVotingWidget',
            'settings': json.dumps({"votable_root_id": subidea_1_1.uri()})
        })
    assert new_widget_loc.status_code == 201

    # Get the widget from the db
    db.flush()
    widget_uri = new_widget_loc.location
    new_widget = Widget.get_instance(widget_uri)
    assert new_widget
    assert new_widget.base_idea == subidea_1_1
    db.expire(new_widget, ('criteria', 'votable_ideas', 'vote_specifications'))

    # Get the widget from the api
    widget_rep = test_app.get(local_to_absolute(widget_uri),
                              headers={"Accept": "application/json"})
    assert widget_rep.status_code == 200
    widget_rep = widget_rep.json
    votespecs_url = widget_rep.get('votespecs_url', None)
    assert votespecs_url
    votespecs_url = local_to_absolute(votespecs_url)

    # Add a first criterion
    vote_spec_1 = {
        '@type': 'LickertVoteSpecification',
        'minimum': 0,
        'maximum': 1,
        'criterion_idea': criterion_1.uri()
    }
    new_vote_spec_loc = test_app.post(votespecs_url,
                                      json.dumps(vote_spec_1),
                                      headers=JSON_HEADER)
    assert new_vote_spec_loc.status_code == 201
    new_vote_spec_uri = new_vote_spec_loc.location
    new_vote_spec = AbstractVoteSpecification.get_instance(new_vote_spec_uri)
    assert new_vote_spec

    # and another one
    vote_spec_2 = {
        '@type': 'BinaryVoteSpecification',
        'criterion_idea': criterion_2.uri()
    }
    new_vote_spec_loc = test_app.post(votespecs_url,
                                      json.dumps(vote_spec_2),
                                      headers=JSON_HEADER)
    assert new_vote_spec_loc.status_code == 201
    new_vote_spec_uri = new_vote_spec_loc.location
    new_vote_spec = AbstractVoteSpecification.get_instance(new_vote_spec_uri)
    assert new_vote_spec

    # and another one
    vote_spec_3 = {
        '@type': 'MultipleChoiceVoteSpecification',
        'num_choices': 5,
        'criterion_idea': criterion_3.uri()
    }
    new_vote_spec_loc = test_app.post(votespecs_url,
                                      json.dumps(vote_spec_3),
                                      headers=JSON_HEADER)
    assert new_vote_spec_loc.status_code == 201
    new_vote_spec_uri = new_vote_spec_loc.location
    new_vote_spec = AbstractVoteSpecification.get_instance(new_vote_spec_uri)
    assert new_vote_spec

    # Get an updated widget_rep with target
    widget_rep = test_app.get(local_to_absolute(widget_uri),
                              {'target': subidea_1_1.uri()},
                              headers={"Accept": "application/json"})
    assert widget_rep.status_code == 200
    widget_rep = widget_rep.json
    voting_urls = widget_rep['voting_urls']
    vote_spec_reps = widget_rep['vote_specifications']
    assert voting_urls

    # User votes should be empty
    user_votes_url = local_to_absolute(widget_rep['user_votes_url'])
    test = test_app.get(user_votes_url)
    assert test.status_code == 200
    assert len(test.json) == 0

    # Get the voting endpoint for each vote_spec, and post a vote.
    # Here we're using the voting_urls of the widget based on a single target;
    # The alternative is to look at the voting_urls of a vote_spec
    # and to get an url per target. The end result should be the same.
    for i, (vote_spec_id, voting_url) in enumerate(voting_urls.iteritems()):
        voting_url = local_to_absolute(voting_url)
        for spec in vote_spec_reps:
            if spec['@id'] == vote_spec_id:
                break
        else:
            assert False, "vote spec %s in voting_urls "\
                "but not in vote_specifications" % (vote_spec_id)
        vote_type = spec['vote_class']
        if vote_type == 'LickertIdeaVote':
            vote_range = spec['maximum'] - spec['minimum']
            value = spec['minimum'] + (i % vote_range)
        elif vote_type == 'BinaryIdeaVote':
            value = True
        elif vote_type == 'MultipleChoiceIdeaVote':
            value = (i % spec['num_choices'])

        test = test_app.post(voting_url,
                             json.dumps({
                                 "@type": vote_type,
                                 "value": value,
                             }),
                             headers=JSON_HEADER)
        assert test.status_code == 201

    # Get them back
    test = test_app.get(user_votes_url)
    assert test.status_code == 200
    assert len(test.json) == len(vote_spec_reps)

    # Add votes for another user
    # TODO
    # Get vote results.
    vote_results_urls = widget_rep['voting_results_by_spec_url']
    for spec_rep in vote_spec_reps:
        assert spec_rep['@id'] in vote_results_urls
        vote_results_url = vote_results_urls.get(spec_rep['@id'], None)
        assert vote_results_url
        vote_results = test_app.get(local_to_absolute(vote_results_url))
        assert vote_results.status_code == 200
        vote_results = vote_results.json
        assert vote_results[subidea_1_1.uri()]['n'] == 1
        if spec_rep['@type'] == "LickertVoteSpecification":
            assert vote_results[subidea_1_1.uri()]['avg'] == 0
    return
    # So far so good, rest to be done.

    # Change my mind
    criterion_key = criteria[0].uri()
    voting_url = local_to_absolute(voting_urls[criterion_key])
    test_app.post(voting_url, {"type": "LickertIdeaVote", "value": 10})
    votes = db.query(AbstractIdeaVote).filter_by(
        voter_id=admin_user.id,
        idea_id=subidea_1_1.id,
        criterion_id=criteria[0].id).all()
    assert len(votes) == 2
    assert len([v for v in votes if v.is_tombstone]) == 1
    for v in votes:
        assert v.widget_id == new_widget.id
    # Get vote results again.
    vote_results_urls = widget_rep['voting_results_by_spec_url']
    for spec_rep in vote_spec_reps:
        assert spec_rep['@id'] in vote_results_urls
        vote_results_url = vote_results_urls.get(spec_rep['@id'], None)
        assert vote_results_url
        vote_results = test_app.get(local_to_absolute(vote_results_url))
        assert vote_results.status_code == 200
        vote_results = vote_results.json
        assert vote_results[subidea_1_1.uri()]['n'] == 1
        if spec_rep['@type'] == "LickertVoteSpecification":
            assert vote_results[subidea_1_1.uri()]['avg'] == 10

    # ideas_data = test_app.get('/api/v1/discussion/%d/ideas' % discussion.id)
    # assert ideas_data.status_code == 200
    # print ideas_data

    def fin():
        print "finalizer test_voting_widget"
        new_widget.delete()
        # this should cascade to specs and votes
        test_session.flush()

    request.addfinalizer(fin)
Beispiel #5
0
def test_inspiration_widget(discussion, test_app, subidea_1, subidea_1_1,
                            participant1_user, test_session):
    # Post the initial configuration
    format = lambda x: x.strftime('%Y-%m-%dT%H:%M:%S')
    new_widget_loc = test_app.post(
        '/data/Discussion/%d/widgets' % (discussion.id, ), {
            'type': 'InspirationWidget',
            'settings': json.dumps({'idea': 'local:Idea/%d' % (subidea_1.id)})
        })
    assert new_widget_loc.status_code == 201

    # Get the widget from the db
    discussion.db.flush()
    widget_uri = new_widget_loc.location
    new_widget = Widget.get_instance(widget_uri)
    assert new_widget
    assert new_widget.base_idea == subidea_1
    widget_id = new_widget.id

    # There should be a link
    widget_link = discussion.db.query(BaseIdeaWidgetLink).filter_by(
        idea_id=subidea_1.id, widget_id=widget_id).all()
    assert widget_link
    assert len(widget_link) == 1

    # Get the widget from the api
    widget_rep = test_app.get(local_to_absolute(widget_uri),
                              headers={"Accept": "application/json"})
    assert widget_rep.status_code == 200
    widget_rep = widget_rep.json
    print widget_rep
    assert 'messages_url' in widget_rep
    assert 'ideas_url' in widget_rep
    assert 'user' in widget_rep

    # Get the list of new ideas
    # should be empty, despite the idea having a non-widget child
    idea_endpoint = local_to_absolute(widget_rep['ideas_url'])
    idea_hiding_endpoint = local_to_absolute(widget_rep['ideas_hiding_url'])
    test = test_app.get(idea_endpoint)
    assert test.status_code == 200
    assert test.json == []

    discussion.db.flush()
    assert new_widget.base_idea == subidea_1
    return

    # WEIRD virtuoso crash in the tests here,
    # dependent on previous tests being run.
    ancestor_widgets = test_app.get(
        '/data/Discussion/%d/ideas/%d/ancestor_inspiration_widgets/' %
        (discussion.id, subidea_1_1.id))
    assert ancestor_widgets.status_code == 200
    ancestor_widgets_rep = ancestor_widgets.json
    assert new_widget_loc.location in ancestor_widgets_rep

    # TODO. ajouter la collection descendant_ideas.
    # Comment déduire cet URL du widget????
    r = test_app.post(
        '/data/Discussion/%d/widgets/%d/base_idea_descendants/%d/linkedposts' %
        (discussion.id, widget_id, subidea_1_1.id), {
            "type": "WidgetPost",
            "body": {
                "@type":
                "LangString",
                "entries": [{
                    "@type": "LangStringEntry",
                    "value": "body",
                    "@language": "en"
                }]
            },
            "creator_id": participant1_user.id,
            "metadata_json": {
                "inspiration_url":
                "https://www.youtube.com/watch?v=7E2FUSYO374"
            }
        })
    assert r.ok
    post_location = r.location
    post = Post.get_instance(post_location)
    assert post
    assert post.widget
    assert post.metadata_json['inspiration_url']
Beispiel #6
0
def test_creativity_session_widget(discussion, test_app, subidea_1,
                                   subidea_1_1, participant1_user,
                                   test_session, request):
    # Post the initial configuration
    format = lambda x: x.strftime('%Y-%m-%dT%H:%M:%S')
    new_widget_loc = test_app.post_json(
        '/data/Discussion/%d/widgets' % (discussion.id, ), {
            '@type': 'CreativitySessionWidget',
            'settings': {
                'idea':
                'local:Idea/%d' % (subidea_1.id),
                'notifications':
                [{
                    'start': '2014-01-01T00:00:00',
                    'end': format(datetime.utcnow() + timedelta(1)),
                    'message': 'creativity_session'
                }, {
                    'start': format(datetime.utcnow() + timedelta(1)),
                    'end': format(datetime.utcnow() + timedelta(2)),
                    'message': 'creativity_session'
                }]
            }
        })
    assert new_widget_loc.status_code == 201
    # Get the widget from the db
    discussion.db.flush()
    new_widget = Widget.get_instance(new_widget_loc.location)
    assert new_widget
    assert new_widget.base_idea == subidea_1
    assert not new_widget.generated_ideas
    widget_id = new_widget.id
    # There should be a link
    widget_uri = new_widget.uri()
    widget_link = discussion.db.query(BaseIdeaWidgetLink).filter_by(
        idea_id=subidea_1.id, widget_id=widget_id).all()
    assert widget_link
    assert len(widget_link) == 1
    # Get the widget from the api
    widget_rep = test_app.get(local_to_absolute(widget_uri),
                              headers={"Accept": "application/json"})
    assert widget_rep.status_code == 200
    widget_rep = widget_rep.json
    print widget_rep
    assert 'messages_url' in widget_rep
    assert 'ideas_url' in widget_rep
    assert 'user' in widget_rep
    # Get the list of new ideas
    # should be empty, despite the idea having a non-widget child
    idea_endpoint = local_to_absolute(widget_rep['ideas_url'])
    idea_hiding_endpoint = local_to_absolute(widget_rep['ideas_hiding_url'])
    test = test_app.get(idea_endpoint)
    assert test.status_code == 200
    assert test.json == []

    discussion.db.flush()
    assert new_widget.base_idea == subidea_1
    ctx_url = "http://example.com/cardgame.xml#card_1"
    # Create a new sub-idea
    new_idea_create = test_app.post_json(
        idea_hiding_endpoint, {
            "@type": "Idea",
            "short_title": "This is a brand new idea",
            "context_url": ctx_url
        })
    assert new_idea_create.status_code == 201
    # Get the sub-idea from the db
    discussion.db.flush()
    assert new_widget.base_idea == subidea_1
    new_idea1_id = new_idea_create.location
    new_idea1 = Idea.get_instance(new_idea1_id)
    assert new_idea1.proposed_in_post
    assert new_idea1 in new_widget.generated_ideas
    assert new_idea1.hidden
    assert new_idea1.proposed_in_post.hidden
    assert not subidea_1.hidden

    # Get the sub-idea from the api
    new_idea1_rep = test_app.get(local_to_absolute(new_idea_create.location),
                                 headers={"Accept": "application/json"})
    assert new_idea1_rep.status_code == 200
    new_idea1_rep = new_idea1_rep.json

    # It should have a link to the root idea
    idea_link = discussion.db.query(IdeaLink).filter_by(
        source_id=subidea_1.id, target_id=new_idea1.id).one()
    assert idea_link

    # It should have a link to the widget
    widget_link = discussion.db.query(GeneratedIdeaWidgetLink).filter_by(
        idea_id=new_idea1.id, widget_id=widget_id).all()
    assert widget_link
    assert len(widget_link) == 1

    # It should be linked to its creating post.
    content_link = discussion.db.query(IdeaContentWidgetLink).filter_by(
        idea_id=new_idea1.id,
        content_id=new_idea1.proposed_in_post.id).first()
    assert content_link

    # The new idea should now be in the collection api
    test = test_app.get(idea_endpoint)
    assert test.status_code == 200
    test = test.json
    assert new_idea1_id in test or new_idea1_id in [x['@id'] for x in test]

    # We should find the context in the new idea
    assert ctx_url in test[0].get('creation_ctx_url', [])
    # TODO: The root idea is included in the above, that's a bug.
    # get the new post endpoint from the idea data
    post_endpoint = new_idea1_rep.get('widget_add_post_endpoint', None)
    assert (post_endpoint and widget_rep["@id"]
            and post_endpoint[widget_rep["@id"]])
    post_endpoint = post_endpoint[widget_rep["@id"]]

    # Create a new post attached to the sub-idea
    new_post_create = test_app.post_json(
        local_to_absolute(post_endpoint), {
            "@type": "AssemblPost",
            "body": {
                "@type":
                "LangString",
                "entries": [{
                    "@type": "LangStringEntry",
                    "value": "body",
                    "@language": "en"
                }]
            },
            "idCreator": participant1_user.uri()
        })
    assert new_post_create.status_code == 201

    # Get the new post from the db
    discussion.db.flush()
    new_post1_id = new_post_create.location
    post = Post.get_instance(new_post1_id)
    assert post.hidden

    # It should have a widget link to the idea.
    post_widget_link = discussion.db.query(IdeaContentWidgetLink).filter_by(
        content_id=post.id, idea_id=new_idea1.id).one()

    # It should be linked to the idea.
    content_link = discussion.db.query(IdeaContentWidgetLink).filter_by(
        idea_id=new_idea1.id, content_id=post.id).first()
    assert content_link

    # TODO: get the semantic data in tests.
    # assert subidea_1.id in Idea.get_idea_ids_showing_post(new_post1_id)
    # It should be a child of the proposing post
    assert post.parent == new_idea1.proposed_in_post

    # The new post should now be in the collection api
    test = test_app.get(local_to_absolute(post_endpoint))
    assert test.status_code == 200
    assert new_post1_id in test.json or new_post1_id in [
        x['@id'] for x in test.json
    ]

    # Get the new post from the api
    new_post1_rep = test_app.get(local_to_absolute(new_post_create.location),
                                 headers={"Accept": "application/json"})
    assert new_post1_rep.status_code == 200

    # It should mention its idea
    print new_post1_rep.json
    assert new_idea1_id in new_post1_rep.json['widget_ideas']

    new_post1 = Post.get_instance(new_post1_id)
    assert new_post1.hidden
    new_idea1 = Idea.get_instance(new_idea1_id)
    assert new_idea1.hidden

    # Create a second idea
    new_idea_create = test_app.post_json(
        idea_hiding_endpoint, {
            "@type": "Idea",
            "short_title": "This is another new idea"
        })
    assert new_idea_create.status_code == 201
    # Get the sub-idea from the db
    discussion.db.flush()
    new_idea2_id = new_idea_create.location

    # Approve the first but not the second idea
    confirm_idea_url = local_to_absolute(widget_rep['confirm_ideas_url'])
    confirm = test_app.post_json(confirm_idea_url, {"ids": [new_idea1_id]})
    assert confirm.status_code == 200
    discussion.db.flush()

    # Get it back
    get_back = test_app.get(confirm_idea_url)
    assert get_back.status_code == 200

    # The first idea should now be unhidden, but not the second
    assert get_back.json == [new_idea1_id]
    new_idea1 = Idea.get_instance(new_idea1_id)
    assert not new_idea1.hidden
    new_idea2 = Idea.get_instance(new_idea2_id)
    assert new_idea2.hidden
    assert new_idea2.proposed_in_post

    # The second idea was not proposed in public
    assert new_idea2.proposed_in_post.hidden

    # The root ideas should not be hidden.
    subidea_1 = Idea.get_instance(subidea_1.id)
    assert not subidea_1.hidden

    # Create a second post.
    new_post_create = test_app.post_json(
        local_to_absolute(post_endpoint), {
            "@type": "AssemblPost",
            "body": {
                "@type":
                "LangString",
                "entries": [{
                    "@type": "LangStringEntry",
                    "value": "body",
                    "@language": "en"
                }]
            },
            "idCreator": participant1_user.uri()
        })
    assert new_post_create.status_code == 201
    discussion.db.flush()
    new_post2_id = new_post_create.location

    # Approve the first but not the second idea
    confirm_messages_url = local_to_absolute(
        widget_rep['confirm_messages_url'])
    confirm = test_app.post_json(confirm_messages_url, {"ids": [new_post1_id]})
    assert confirm.status_code == 200
    discussion.db.flush()

    # Get it back
    get_back = test_app.get(confirm_messages_url)
    assert get_back.status_code == 200
    assert get_back.json == [new_post1_id]

    # The first idea should now be unhidden, but not the second
    new_post1 = Post.get_instance(new_post1_id)
    assert not new_post1.hidden
    new_post2 = Post.get_instance(new_post2_id)

    def clear_data():
        print "finalizing test data"
        test_session.delete(new_post1)
        test_session.delete(new_post2)
        test_session.delete(new_idea1.proposed_in_post)
        test_session.delete(new_idea2.proposed_in_post)
        test_session.flush()

    request.addfinalizer(clear_data)
    assert new_post2.hidden

    # Get the notifications
    notifications = test_app.get('/data/Discussion/%d/notifications' %
                                 discussion.id)
    assert notifications.status_code == 200
    notifications = notifications.json

    # Only one active session
    assert len(notifications) == 1
    notification = notifications[0]
    print notification
    assert notification['widget_url']
    assert notification['time_to_end'] > 23 * 60 * 60
    assert notification['num_participants'] == 2  # participant and admin
    assert notification['num_ideas'] == 2
Beispiel #7
0
def test_inspiration_widget(discussion, test_app, subidea_1, subidea_1_1,
                            participant1_user, test_session):
    # Post the initial configuration
    format = lambda x: x.strftime('%Y-%m-%dT%H:%M:%S')
    new_widget_loc = test_app.post_json(
        '/data/Conversation/%d/widgets' % (discussion.id, ), {
            '@type': 'InspirationWidget',
            'settings': {
                'idea': 'local:GenericIdeaNode/%d' % (subidea_1.id)
            }
        })
    assert new_widget_loc.status_code == 201

    # Get the widget from the db
    discussion.db.flush()
    widget_uri = new_widget_loc.location
    new_widget = Widget.get_instance(widget_uri)
    assert new_widget
    assert new_widget.base_idea == subidea_1
    widget_id = new_widget.id

    # There should be a link
    widget_link = discussion.db.query(BaseIdeaWidgetLink).filter_by(
        idea_id=subidea_1.id, widget_id=widget_id).all()
    assert widget_link
    assert len(widget_link) == 1

    # Get the widget from the api
    widget_rep = test_app.get(local_to_absolute(widget_uri),
                              headers={"Accept": "application/json"})
    assert widget_rep.status_code == 200
    widget_rep = widget_rep.json
    print(widget_rep)
    assert 'messages_url' in widget_rep
    assert 'ideas_url' in widget_rep
    assert 'user' in widget_rep

    # Get the list of new ideas
    # should be empty, despite the idea having a non-widget child
    idea_endpoint = local_to_absolute(widget_rep['ideas_url'])
    idea_hiding_endpoint = local_to_absolute(widget_rep['ideas_hiding_url'])
    test = test_app.get(idea_endpoint, headers=accept_json)
    assert test.status_code == 200
    assert test.json == []

    discussion.db.flush()
    assert new_widget.base_idea == subidea_1
    endpoint = widget_rep.get('base_idea', {}).get('widget_add_post_endpoint',
                                                   {}).get(widget_uri, None)
    assert endpoint
    endpoint = '/data/' + endpoint[6:]

    # TODO. ajouter la collection descendant_ideas.
    # Comment déduire cet URL du widget????
    r = test_app.post_json(
        endpoint, {
            "@type": "WidgetPost",
            "body": {
                "@type":
                "LangString",
                "entries": [{
                    "@type": "LangStringEntry",
                    "value": "body",
                    "@language": "en"
                }]
            },
            "creator_id": participant1_user.id,
            "metadata_json": {
                "inspiration_url":
                "https://www.youtube.com/watch?v=7E2FUSYO374"
            }
        })
    assert r.status_code == 201
    post_location = r.location
    post = Post.get_instance(post_location)
    assert post
    assert post.widget
    assert post.metadata_json['inspiration_url']
    assert post.idea_links_of_content
    content_idea_link = post.idea_links_of_content[0]
    assert content_idea_link.idea == subidea_1
    post.delete()
    post.db.flush()
Beispiel #8
0
def _get_ideas_real(request, view_def=None, ids=None, user_id=None):
    discussion = request.discussion
    user_id = user_id or Everyone
    # optimization: Recursive widget links.
    from assembl.models import (Widget, IdeaWidgetLink,
                                IdeaDescendantsShowingWidgetLink)
    universal_widget_links = []
    by_idea_widget_links = defaultdict(list)
    widget_links = discussion.db.query(IdeaWidgetLink).join(Widget).join(
        Discussion).filter(
            Widget.test_active(), Discussion.id == discussion.id,
            IdeaDescendantsShowingWidgetLink.polymorphic_filter()).options(
                joinedload(IdeaWidgetLink.idea)).all()
    for wlink in widget_links:
        if isinstance(wlink.idea, RootIdea):
            universal_widget_links.append({
                '@type':
                wlink.external_typename(),
                'widget':
                Widget.uri_generic(wlink.widget_id)
            })
        else:
            for id in wlink.idea.get_all_descendants(True):
                by_idea_widget_links[Idea.uri_generic(id)].append({
                    '@type':
                    wlink.external_typename(),
                    'widget':
                    Widget.uri_generic(wlink.widget_id)
                })

    next_synthesis_id = discussion.get_next_synthesis_id()
    ideas = discussion.db.query(Idea).filter_by(discussion=discussion)

    ideas = ideas.outerjoin(
        SubGraphIdeaAssociation,
        and_(SubGraphIdeaAssociation.sub_graph_id == next_synthesis_id,
             SubGraphIdeaAssociation.idea_id == Idea.id))

    ideas = ideas.outerjoin(IdeaLink, IdeaLink.target_id == Idea.id)

    ideas = ideas.order_by(IdeaLink.order, Idea.creation_date)

    if ids:
        ids = [Idea.get_database_id(id) for id in ids]
        ideas = ideas.filter(Idea.id.in_(ids))
    # remove tombstones
    ideas = ideas.filter(and_(*Idea.base_conditions()))
    ideas = ideas.options(
        joinedload(Idea.source_links),
        subqueryload(Idea.attachments).joinedload("document"),
        subqueryload(Idea.widget_links),
        joinedload(Idea.title).subqueryload("entries"),
        joinedload(Idea.synthesis_title).subqueryload("entries"),
        joinedload(Idea.description).subqueryload("entries"),
        undefer(Idea.num_children))

    permissions = request.permissions
    Idea.prepare_counters(discussion.id, True)
    # ideas = list(ideas)
    # import cProfile
    # cProfile.runctx('''retval = [idea.generic_json(None, %d, %s)
    #           for idea in ideas]''' % (user_id, permissions),
    #           globals(), locals(), 'json_stats')
    retval = [
        idea.generic_json(view_def, user_id, permissions) for idea in ideas
    ]
    retval = [x for x in retval if x is not None]
    for r in retval:
        if r.get('widget_links', None) is not None:
            links = r['widget_links'][:]
            links.extend(universal_widget_links)
            links.extend(by_idea_widget_links[r['@id']])
            r['active_widget_links'] = links
    return retval
Beispiel #9
0
def DISABLEDtest_voting_widget_criteria(
        discussion, test_app, subidea_1_1, criterion_1, criterion_2,
        criterion_3, admin_user, participant1_user,
        test_session):
    # Post the initial configuration
    db = discussion.db
    criteria = (criterion_1, criterion_2)
    criteria_def = [
        {
            "@id": criterion.uri(),
            "short_title": criterion.short_title
        } for criterion in criteria
    ]
    new_widget_loc = test_app.post(
        '/data/Discussion/%d/widgets' % (discussion.id,), {
            'type': 'MultiCriterionVotingWidget',
            'settings': json.dumps({
                "criteria": criteria_def
            })
        })
    assert new_widget_loc.status_code == 201
    # Get the widget from the db
    db.flush()
    new_widget = Widget.get_instance(new_widget_loc.location)
    assert new_widget
    db.expire(new_widget, ('criteria', ))
    # Get the widget from the api
    widget_rep = test_app.get(
        local_to_absolute(new_widget.uri()),
        {'target': subidea_1_1.uri()},
        headers={"Accept": "application/json"}
    )
    assert widget_rep.status_code == 200
    widget_rep = widget_rep.json
    voting_urls = widget_rep['voting_urls']
    assert voting_urls
    assert widget_rep['criteria']
    assert widget_rep['criteria_url']

    # Note: At this point, we have two copies of the criteria in the rep.
    # One is the full ideas in widget_rep['criteria'], the other is
    # as specified originally in widget_rep['settings']['criteria'].
    # In what follows I'll use the former.

    # The criteria should also be in the criteria url
    criteria_url = local_to_absolute(widget_rep['criteria_url'])
    test = test_app.get(criteria_url)
    assert test.status_code == 200
    assert len(test.json) == 2
    assert {x['@id'] for x in test.json} == {c.uri() for c in criteria}
    assert test.json == widget_rep['criteria']

    # Set a new set of criteria
    criteria = (criterion_2, criterion_3)
    criteria_def = [
        {
            "@id": criterion.uri(),
            "short_title": criterion.short_title
        } for criterion in criteria
    ]
    test_app.put(criteria_url, json.dumps(criteria_def),
                 headers=JSON_HEADER)
    db.flush()
    db.expire(new_widget, ('criteria', ))

    # Get them back
    test = test_app.get(criteria_url)
    assert test.status_code == 200
    assert len(test.json) == 2
    assert {x['@id'] for x in test.json} == {c.uri() for c in criteria}
Beispiel #10
0
def test_voting_widget(
        discussion, test_app, subidea_1_1, criterion_1, criterion_2,
        criterion_3, admin_user, participant1_user,
        test_session, request):
    # Post the initial configuration
    db = discussion.db
    criteria = (criterion_1, criterion_2, criterion_3)
    new_widget_loc = test_app.post(
        '/data/Discussion/%d/widgets' % (discussion.id,), {
            'type': 'MultiCriterionVotingWidget',
            'settings': json.dumps({
                "votable_root_id": subidea_1_1.uri()
            })
        })
    assert new_widget_loc.status_code == 201

    # Get the widget from the db
    db.flush()
    widget_uri = new_widget_loc.location
    new_widget = Widget.get_instance(widget_uri)
    assert new_widget
    assert new_widget.base_idea == subidea_1_1
    db.expire(new_widget, ('criteria', 'votable_ideas', 'vote_specifications'))

    # Get the widget from the api
    widget_rep = test_app.get(
        local_to_absolute(widget_uri),
        headers={"Accept": "application/json"}
    )
    assert widget_rep.status_code == 200
    widget_rep = widget_rep.json
    votespecs_url = widget_rep.get('votespecs_url', None)
    assert votespecs_url
    votespecs_url = local_to_absolute(votespecs_url)

    # Add a first criterion
    vote_spec_1 = {
        '@type': 'LickertVoteSpecification',
        'minimum': 0,
        'maximum': 1,
        'criterion_idea': criterion_1.uri()
    }
    new_vote_spec_loc = test_app.post(
        votespecs_url, json.dumps(vote_spec_1),
        headers=JSON_HEADER)
    assert new_vote_spec_loc.status_code == 201
    new_vote_spec_uri = new_vote_spec_loc.location
    new_vote_spec = AbstractVoteSpecification.get_instance(new_vote_spec_uri)
    assert new_vote_spec

    # and another one
    vote_spec_2 = {
        '@type': 'BinaryVoteSpecification',
        'criterion_idea': criterion_2.uri()
    }
    new_vote_spec_loc = test_app.post(
        votespecs_url, json.dumps(vote_spec_2),
        headers=JSON_HEADER)
    assert new_vote_spec_loc.status_code == 201
    new_vote_spec_uri = new_vote_spec_loc.location
    new_vote_spec = AbstractVoteSpecification.get_instance(new_vote_spec_uri)
    assert new_vote_spec

    # and another one
    vote_spec_3 = {
        '@type': 'MultipleChoiceVoteSpecification',
        'num_choices': 5,
        'criterion_idea': criterion_3.uri()
    }
    new_vote_spec_loc = test_app.post(
        votespecs_url, json.dumps(vote_spec_3),
        headers=JSON_HEADER)
    assert new_vote_spec_loc.status_code == 201
    new_vote_spec_uri = new_vote_spec_loc.location
    new_vote_spec = AbstractVoteSpecification.get_instance(new_vote_spec_uri)
    assert new_vote_spec

    # Get an updated widget_rep with target
    widget_rep = test_app.get(
        local_to_absolute(widget_uri),
        {'target': subidea_1_1.uri()},
        headers={"Accept": "application/json"}
    )
    assert widget_rep.status_code == 200
    widget_rep = widget_rep.json
    voting_urls = widget_rep['voting_urls']
    vote_spec_reps = widget_rep['vote_specifications']
    assert voting_urls

    # User votes should be empty
    user_votes_url = local_to_absolute(widget_rep['user_votes_url'])
    test = test_app.get(user_votes_url)
    assert test.status_code == 200
    assert len(test.json) == 0

    # Get the voting endpoint for each vote_spec, and post a vote.
    # Here we're using the voting_urls of the widget based on a single target;
    # The alternative is to look at the voting_urls of a vote_spec
    # and to get an url per target. The end result should be the same.
    for i, (vote_spec_id, voting_url) in enumerate(voting_urls.iteritems()):
        voting_url = local_to_absolute(voting_url)
        for spec in vote_spec_reps:
            if spec['@id'] == vote_spec_id:
                break
        else:
            assert False, "vote spec %s in voting_urls "\
                "but not in vote_specifications" % (vote_spec_id)
        vote_type = spec['vote_class']
        if vote_type == 'LickertIdeaVote':
            vote_range = spec['maximum'] - spec['minimum']
            value = spec['minimum'] + (i % vote_range)
        elif vote_type == 'BinaryIdeaVote':
            value = True
        elif vote_type == 'MultipleChoiceIdeaVote':
            value = (i % spec['num_choices'])

        test = test_app.post(voting_url, json.dumps({
            "@type": vote_type,
            "value": value,
        }), headers=JSON_HEADER)
        assert test.status_code == 201

    # Get them back
    test = test_app.get(user_votes_url)
    assert test.status_code == 200
    assert len(test.json) == len(vote_spec_reps)

    # Add votes for another user
    # TODO
    # Get vote results.
    vote_results_urls = widget_rep['voting_results_by_spec_url']
    for spec_rep in vote_spec_reps:
        assert spec_rep['@id'] in vote_results_urls
        vote_results_url = vote_results_urls.get(spec_rep['@id'], None)
        assert vote_results_url
        vote_results = test_app.get(local_to_absolute(vote_results_url))
        assert vote_results.status_code == 200
        vote_results = vote_results.json
        assert vote_results[subidea_1_1.uri()]['n'] == 1
        if spec_rep['@type'] == "LickertVoteSpecification":
            assert vote_results[subidea_1_1.uri()]['avg'] == 0
    return
    # So far so good, rest to be done.

    # Change my mind
    criterion_key = criteria[0].uri()
    voting_url = local_to_absolute(voting_urls[criterion_key])
    test_app.post(voting_url, {
        "type": "LickertIdeaVote", "value": 10})
    votes = db.query(AbstractIdeaVote).filter_by(
        voter_id=admin_user.id, idea_id=subidea_1_1.id,
        criterion_id=criteria[0].id).all()
    assert len(votes) == 2
    assert len([v for v in votes if v.is_tombstone]) == 1
    for v in votes:
        assert v.widget_id == new_widget.id
    # Get vote results again.
    vote_results_urls = widget_rep['voting_results_by_spec_url']
    for spec_rep in vote_spec_reps:
        assert spec_rep['@id'] in vote_results_urls
        vote_results_url = vote_results_urls.get(spec_rep['@id'], None)
        assert vote_results_url
        vote_results = test_app.get(local_to_absolute(vote_results_url))
        assert vote_results.status_code == 200
        vote_results = vote_results.json
        assert vote_results[subidea_1_1.uri()]['n'] == 1
        if spec_rep['@type'] == "LickertVoteSpecification":
            assert vote_results[subidea_1_1.uri()]['avg'] == 10

    # ideas_data = test_app.get('/api/v1/discussion/%d/ideas' % discussion.id)
    # assert ideas_data.status_code == 200
    # print ideas_data

    def fin():
        print "finalizer test_voting_widget"
        new_widget.delete()
        # this should cascade to specs and votes
        test_session.flush()
    request.addfinalizer(fin)
Beispiel #11
0
def test_inspiration_widget(
        discussion, test_app, subidea_1, subidea_1_1,
        participant1_user, test_session):
    # Post the initial configuration
    format = lambda x: x.strftime('%Y-%m-%dT%H:%M:%S')
    new_widget_loc = test_app.post(
        '/data/Discussion/%d/widgets' % (discussion.id,), {
            'type': 'InspirationWidget',
            'settings': json.dumps({
                'idea': 'local:Idea/%d' % (subidea_1.id)
            })
        })
    assert new_widget_loc.status_code == 201

    # Get the widget from the db
    discussion.db.flush()
    widget_uri = new_widget_loc.location
    new_widget = Widget.get_instance(widget_uri)
    assert new_widget
    assert new_widget.base_idea == subidea_1
    widget_id = new_widget.id

    # There should be a link
    widget_link = discussion.db.query(BaseIdeaWidgetLink).filter_by(
        idea_id=subidea_1.id, widget_id=widget_id).all()
    assert widget_link
    assert len(widget_link) == 1

    # Get the widget from the api
    widget_rep = test_app.get(
        local_to_absolute(widget_uri),
        headers={"Accept": "application/json"}
    )
    assert widget_rep.status_code == 200
    widget_rep = widget_rep.json
    print widget_rep
    assert 'messages_url' in widget_rep
    assert 'ideas_url' in widget_rep
    assert 'user' in widget_rep

    # Get the list of new ideas
    # should be empty, despite the idea having a non-widget child
    idea_endpoint = local_to_absolute(widget_rep['ideas_url'])
    idea_hiding_endpoint = local_to_absolute(widget_rep['ideas_hiding_url'])
    test = test_app.get(idea_endpoint)
    assert test.status_code == 200
    assert test.json == []

    discussion.db.flush()
    assert new_widget.base_idea == subidea_1
    return

    # WEIRD virtuoso crash in the tests here,
    # dependent on previous tests being run.
    ancestor_widgets = test_app.get(
        '/data/Discussion/%d/ideas/%d/ancestor_inspiration_widgets/' % (
            discussion.id, subidea_1_1.id))
    assert ancestor_widgets.status_code == 200
    ancestor_widgets_rep = ancestor_widgets.json
    assert new_widget_loc.location in ancestor_widgets_rep

    # TODO. ajouter la collection descendant_ideas.
    # Comment déduire cet URL du widget????
    r = test_app.post(
        '/data/Discussion/%d/widgets/%d/base_idea_descendants/%d/linkedposts' %
        (discussion.id, widget_id, subidea_1_1.id), {
            "type": "WidgetPost",
            "body": {"@type": "LangString", "entries": [{
                "@type": "LangStringEntry", "value": "body",
                "@language": "en"
            }]}, "creator_id": participant1_user.id,
            "metadata_json": {
                "inspiration_url":
                    "https://www.youtube.com/watch?v=7E2FUSYO374"}})
    assert r.ok
    post_location = r.location
    post = Post.get_instance(post_location)
    assert post
    assert post.widget
    assert post.metadata_json['inspiration_url']
Beispiel #12
0
def test_creativity_session_widget(
        discussion, test_app, subidea_1, subidea_1_1,
        participant1_user, test_session, request):
    # Post the initial configuration
    format = lambda x: x.strftime('%Y-%m-%dT%H:%M:%S')
    new_widget_loc = test_app.post_json(
        '/data/Discussion/%d/widgets' % (discussion.id,), {
            '@type': 'CreativitySessionWidget',
            'settings': {
                'idea': 'local:Idea/%d' % (subidea_1.id),
                'notifications': [
                    {
                        'start': '2014-01-01T00:00:00',
                        'end': format(datetime.utcnow() + timedelta(1)),
                        'message': 'creativity_session'
                    },
                    {
                        'start': format(datetime.utcnow() + timedelta(1)),
                        'end': format(datetime.utcnow() + timedelta(2)),
                        'message': 'creativity_session'
                    }
                ]
            }
        })
    assert new_widget_loc.status_code == 201
    # Get the widget from the db
    discussion.db.flush()
    new_widget = Widget.get_instance(new_widget_loc.location)
    assert new_widget
    assert new_widget.base_idea == subidea_1
    assert not new_widget.generated_ideas
    widget_id = new_widget.id
    # There should be a link
    widget_uri = new_widget.uri()
    widget_link = discussion.db.query(BaseIdeaWidgetLink).filter_by(
        idea_id=subidea_1.id, widget_id=widget_id).all()
    assert widget_link
    assert len(widget_link) == 1
    # Get the widget from the api
    widget_rep = test_app.get(
        local_to_absolute(widget_uri),
        headers={"Accept": "application/json"}
    )
    assert widget_rep.status_code == 200
    widget_rep = widget_rep.json
    print widget_rep
    assert 'messages_url' in widget_rep
    assert 'ideas_url' in widget_rep
    assert 'user' in widget_rep
    # Get the list of new ideas
    # should be empty, despite the idea having a non-widget child
    idea_endpoint = local_to_absolute(widget_rep['ideas_url'])
    idea_hiding_endpoint = local_to_absolute(widget_rep['ideas_hiding_url'])
    test = test_app.get(idea_endpoint)
    assert test.status_code == 200
    assert test.json == []

    discussion.db.flush()
    assert new_widget.base_idea == subidea_1
    ctx_url = "http://example.com/cardgame.xml#card_1"
    # Create a new sub-idea
    new_idea_create = test_app.post_json(idea_hiding_endpoint, {
        "@type": "Idea", "short_title": "This is a brand new idea",
        "context_url": ctx_url
    })
    assert new_idea_create.status_code == 201
    # Get the sub-idea from the db
    discussion.db.flush()
    assert new_widget.base_idea == subidea_1
    new_idea1_id = new_idea_create.location
    new_idea1 = Idea.get_instance(new_idea1_id)
    assert new_idea1.proposed_in_post
    assert new_idea1 in new_widget.generated_ideas
    assert new_idea1.hidden
    assert new_idea1.proposed_in_post.hidden
    assert not subidea_1.hidden

    # Get the sub-idea from the api
    new_idea1_rep = test_app.get(
        local_to_absolute(new_idea_create.location),
        headers={"Accept": "application/json"}
    )
    assert new_idea1_rep.status_code == 200
    new_idea1_rep = new_idea1_rep.json

    # It should have a link to the root idea
    idea_link = discussion.db.query(IdeaLink).filter_by(
        source_id=subidea_1.id, target_id=new_idea1.id).one()
    assert idea_link

    # It should have a link to the widget
    widget_link = discussion.db.query(GeneratedIdeaWidgetLink).filter_by(
        idea_id=new_idea1.id, widget_id=widget_id).all()
    assert widget_link
    assert len(widget_link) == 1

    # It should be linked to its creating post.
    content_link = discussion.db.query(IdeaContentWidgetLink).filter_by(
        idea_id=new_idea1.id, content_id=new_idea1.proposed_in_post.id).first()
    assert content_link

    # The new idea should now be in the collection api
    test = test_app.get(idea_endpoint)
    assert test.status_code == 200
    test = test.json
    assert new_idea1_id in test or new_idea1_id in [
        x['@id'] for x in test]

    # We should find the context in the new idea
    assert ctx_url in test[0].get('creation_ctx_url', [])
    # TODO: The root idea is included in the above, that's a bug.
    # get the new post endpoint from the idea data
    post_endpoint = new_idea1_rep.get('widget_add_post_endpoint', None)
    assert (post_endpoint and widget_rep["@id"] and
            post_endpoint[widget_rep["@id"]])
    post_endpoint = post_endpoint[widget_rep["@id"]]

    # Create a new post attached to the sub-idea
    new_post_create = test_app.post_json(local_to_absolute(post_endpoint), {
        "@type": "AssemblPost",
        "body": {"@type": "LangString", "entries": [{
            "@type": "LangStringEntry", "value": "body",
            "@language": "en"
        }]}, "idCreator": participant1_user.uri()})
    assert new_post_create.status_code == 201

    # Get the new post from the db
    discussion.db.flush()
    new_post1_id = new_post_create.location
    post = Post.get_instance(new_post1_id)
    assert post.hidden

    # It should have a widget link to the idea.
    post_widget_link = discussion.db.query(IdeaContentWidgetLink).filter_by(
        content_id=post.id, idea_id=new_idea1.id).one()

    # It should be linked to the idea.
    content_link = discussion.db.query(IdeaContentWidgetLink).filter_by(
        idea_id=new_idea1.id, content_id=post.id).first()
    assert content_link

    # TODO: get the semantic data in tests.
    # assert subidea_1.id in Idea.get_idea_ids_showing_post(new_post1_id)
    # It should be a child of the proposing post
    assert post.parent == new_idea1.proposed_in_post

    # The new post should now be in the collection api
    test = test_app.get(local_to_absolute(post_endpoint))
    assert test.status_code == 200
    assert new_post1_id in test.json or new_post1_id in [
        x['@id'] for x in test.json]

    # Get the new post from the api
    new_post1_rep = test_app.get(
        local_to_absolute(new_post_create.location),
        headers={"Accept": "application/json"}
    )
    assert new_post1_rep.status_code == 200

    # It should mention its idea
    print new_post1_rep.json
    assert new_idea1_id in new_post1_rep.json['widget_ideas']

    new_post1 = Post.get_instance(new_post1_id)
    assert new_post1.hidden
    new_idea1 = Idea.get_instance(new_idea1_id)
    assert new_idea1.hidden

    # Create a second idea
    new_idea_create = test_app.post_json(idea_hiding_endpoint, {
        "@type": "Idea", "short_title": "This is another new idea"})
    assert new_idea_create.status_code == 201
    # Get the sub-idea from the db
    discussion.db.flush()
    new_idea2_id = new_idea_create.location

    # Approve the first but not the second idea
    confirm_idea_url = local_to_absolute(widget_rep['confirm_ideas_url'])
    confirm = test_app.post_json(confirm_idea_url, {
        "ids": [new_idea1_id]})
    assert confirm.status_code == 200
    discussion.db.flush()

    # Get it back
    get_back = test_app.get(confirm_idea_url)
    assert get_back.status_code == 200

    # The first idea should now be unhidden, but not the second
    assert get_back.json == [new_idea1_id]
    new_idea1 = Idea.get_instance(new_idea1_id)
    assert not new_idea1.hidden
    new_idea2 = Idea.get_instance(new_idea2_id)
    assert new_idea2.hidden
    assert new_idea2.proposed_in_post

    # The second idea was not proposed in public
    assert new_idea2.proposed_in_post.hidden

    # The root ideas should not be hidden.
    subidea_1 = Idea.get_instance(subidea_1.id)
    assert not subidea_1.hidden

    # Create a second post.
    new_post_create = test_app.post_json(local_to_absolute(post_endpoint), {
        "@type": "AssemblPost",
        "body": {"@type": "LangString", "entries": [{
            "@type": "LangStringEntry", "value": "body",
            "@language": "en"
        }]}, "idCreator": participant1_user.uri()})
    assert new_post_create.status_code == 201
    discussion.db.flush()
    new_post2_id = new_post_create.location

    # Approve the first but not the second idea
    confirm_messages_url = local_to_absolute(
        widget_rep['confirm_messages_url'])
    confirm = test_app.post_json(confirm_messages_url, {
        "ids": [new_post1_id]})
    assert confirm.status_code == 200
    discussion.db.flush()

    # Get it back
    get_back = test_app.get(confirm_messages_url)
    assert get_back.status_code == 200
    assert get_back.json == [new_post1_id]

    # The first idea should now be unhidden, but not the second
    new_post1 = Post.get_instance(new_post1_id)
    assert not new_post1.hidden
    new_post2 = Post.get_instance(new_post2_id)

    def clear_data():
        print "finalizing test data"
        test_session.delete(new_post1)
        test_session.delete(new_post2)
        test_session.delete(new_idea1.proposed_in_post)
        test_session.delete(new_idea2.proposed_in_post)
        test_session.flush()
    request.addfinalizer(clear_data)
    assert new_post2.hidden

    # Get the notifications
    notifications = test_app.get(
        '/data/Discussion/%d/notifications' % discussion.id)
    assert notifications.status_code == 200
    notifications = notifications.json

    # Only one active session
    assert len(notifications) == 1
    notification = notifications[0]
    print notification
    assert notification['widget_url']
    assert notification['time_to_end'] > 23 * 60 * 60
    assert notification['num_participants'] == 2  # participant and admin
    assert notification['num_ideas'] == 2