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
def test_get_ideas(discussion, test_app, synthesis_1, subidea_1_1_1, test_session): all_ideas = test_app.get('/data/Idea') assert all_ideas.status_code == 200 all_ideas = all_ideas.json disc_ideas = test_app.get('/data/Discussion/%d/ideas?view=id_only' % (discussion.id,)) assert disc_ideas.status_code == 200 disc_ideas = disc_ideas.json assert set(all_ideas) == set(disc_ideas) synthesis_ideasassocs = test_app.get( '/data/Discussion/%d/views/%d/idea_assocs?view=id_only' % ( discussion.id, synthesis_1.id)) assert synthesis_ideasassocs.status_code == 200 synthesis_ideasassocs = synthesis_ideasassocs.json syn_ideas = set() for assoc_id in synthesis_ideasassocs: a = SubGraphIdeaAssociation.get_instance(assoc_id) syn_ideas.add(Idea.uri_generic(a.idea_id)) assert syn_ideas < set(disc_ideas) subidea_1_1_1_id = Idea.uri_generic(subidea_1_1_1.id) assert subidea_1_1_1_id in disc_ideas assert subidea_1_1_1_id not in syn_ideas syn_ideas = test_app.get( '/data/Discussion/%d/views/%d/ideas?view=id_only' % ( discussion.id, synthesis_1.id)) assert syn_ideas.status_code == 200 syn_ideas = syn_ideas.json assert set(syn_ideas) < set(disc_ideas) subidea_1_1_1_id = Idea.uri_generic(subidea_1_1_1.id) assert subidea_1_1_1_id in disc_ideas assert subidea_1_1_1_id not in syn_ideas
def create_idea(request): discussion_id = int(request.matchdict['discussion_id']) session = Discussion.default_db discussion = session.query(Discussion).get(int(discussion_id)) user_id = request.authenticated_userid permissions = get_permissions(user_id, discussion.id) idea_data = json.loads(request.body) kwargs = { "discussion": discussion } for key, attr_name in langstring_fields.iteritems(): if key in idea_data: ls_data = idea_data[key] if ls_data is None: continue assert isinstance(ls_data, dict) current = LangString.create_from_json( ls_data, user_id, permissions=permissions) kwargs[attr_name] = current new_idea = Idea(**kwargs) session.add(new_idea) if idea_data['parentId']: parent = Idea.get_instance(idea_data['parentId']) else: parent = discussion.root_idea session.add(IdeaLink(source=parent, target=new_idea, order=idea_data.get('order', 0.0))) session.flush() return {'ok': True, '@id': new_idea.uri()}
def test_get_ideas(discussion, test_app, synthesis_1, subidea_1_1_1, test_session): all_ideas = test_app.get('/data/Idea') assert all_ideas.status_code == 200 all_ideas = all_ideas.json disc_ideas = test_app.get('/data/Discussion/%d/ideas?view=id_only' % (discussion.id, )) assert disc_ideas.status_code == 200 disc_ideas = disc_ideas.json assert set(all_ideas) == set(disc_ideas) synthesis_ideasassocs = test_app.get( '/data/Discussion/%d/views/%d/idea_assocs?view=id_only' % (discussion.id, synthesis_1.id)) assert synthesis_ideasassocs.status_code == 200 synthesis_ideasassocs = synthesis_ideasassocs.json syn_ideas = set() for assoc_id in synthesis_ideasassocs: a = SubGraphIdeaAssociation.get_instance(assoc_id) syn_ideas.add(Idea.uri_generic(a.idea_id)) assert syn_ideas < set(disc_ideas) subidea_1_1_1_id = Idea.uri_generic(subidea_1_1_1.id) assert subidea_1_1_1_id in disc_ideas assert subidea_1_1_1_id not in syn_ideas syn_ideas = test_app.get( '/data/Discussion/%d/views/%d/ideas?view=id_only' % (discussion.id, synthesis_1.id)) assert syn_ideas.status_code == 200 syn_ideas = syn_ideas.json assert set(syn_ideas) < set(disc_ideas) subidea_1_1_1_id = Idea.uri_generic(subidea_1_1_1.id) assert subidea_1_1_1_id in disc_ideas assert subidea_1_1_1_id not in syn_ideas
def create_idea(request): discussion_id = int(request.matchdict['discussion_id']) session = Discussion.default_db discussion = session.query(Discussion).get(int(discussion_id)) idea_data = json.loads(request.body) new_idea = Idea( short_title=idea_data['shortTitle'], long_title=idea_data['longTitle'], discussion=discussion, ) session.add(new_idea) if idea_data['parentId']: parent = Idea.get_instance(idea_data['parentId']) else: parent = discussion.root_idea session.add( IdeaLink(source=parent, target=new_idea, order=idea_data.get('order', 0.0))) session.flush() return {'ok': True, '@id': new_idea.uri()}
def test_get_long_title_on_idea(graphql_request, idea_in_thread_phase): # This is the "What you need to know" idea_id = idea_in_thread_phase from graphene.relay import Node raw_id = int(Node.from_global_id(idea_id)[1]) from assembl.models import Idea idea = Idea.get(raw_id) idea.long_title = u'What you need to know' idea.db.flush() res = schema.execute(u""" query Idea($lang: String!, $id: ID!) { idea: node(id: $id) { ... on Idea { title(lang: $lang) longTitle description(lang: $lang) imgUrl } } } """, context_value=graphql_request, variable_values={ "id": idea_id, "lang": u'en', }) assert json.loads(json.dumps(res.data)) == { u'idea': { u'title': u'Understanding the dynamics and issues', u'longTitle': u'What you need to know', u'description': u'', u'imgUrl': None } }
def subidea_1_1_1_1_2_2(request, discussion, subidea_1_1_1_1_2, test_session): """An Idea fixture with a idealink to subidea_1_1_1_1_2 fixture - root_idea |-> subidea_1 |-> subidea_1_1 |-> subidea_1_1_1 |-> subidea_1_1_1_1 |-> subidea_1_1_1_1 |-> subidea_1_1_1_2 |-> subidea_1_1_1_1_2_1 |-> subidea_1_1_1_1_2_2""" from assembl.models import Idea, IdeaLink i = Idea(short_title=u"Federal programs are ineffective", discussion=discussion) test_session.add(i) l_11112_111122 = IdeaLink(source=subidea_1_1_1_1_2, target=i) test_session.add(l_11112_111122) test_session.flush() def fin(): print "finalizer subidea_1_1_1_1_2_2" test_session.delete(l_11112_111122) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def subidea_1_1_1_1_2_1(request, discussion, subidea_1_1_1_1_2, test_session): """An Idea fixture with a idealink to subidea_1_1_1_1_2 fixture - root_idea |-> subidea_1 |-> subidea_1_1 |-> subidea_1_1_1 |-> subidea_1_1_1_1 |-> subidea_1_1_1_1 |-> subidea_1_1_1_2 |-> subidea_1_1_1_1_2_1""" from assembl.models import Idea, IdeaLink i = Idea(short_title=u"Bad for the environment", discussion=discussion) test_session.add(i) l_11112_111121 = IdeaLink(source=subidea_1_1_1_1_2, target=i) test_session.add(l_11112_111121) test_session.flush() def fin(): print "finalizer subidea_1_1_1_1_2_1" test_session.delete(l_11112_111121) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def subidea_1_1_1_1_2(request, discussion, subidea_1_1_1_1, test_session): """An Idea fixture with a idealink to subidea_1_1_1_1 fixture - root_idea |-> subidea_1 |-> subidea_1_1 |-> subidea_1_1_1 |-> subidea_1_1_1_1 |-> subidea_1_1_1_1 |-> subidea_1_1_1_2""" from assembl.models import Idea, IdeaLink i = Idea(short_title=u"Environmental program cuts", discussion=discussion) test_session.add(i) l_1111_11112 = IdeaLink(source=subidea_1_1_1_1, target=i) test_session.add(l_1111_11112) test_session.flush() def fin(): print "finalizer subidea_1_1_1_1_2" test_session.delete(l_1111_11112) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def subidea_1_1_1(request, discussion, subidea_1_1, test_session): """An Idea fixture with a idealink to subidea_1_1 fixture:: root_idea |-> subidea_1 |-> subidea_1_1 |-> subidea_1_1_1""" from assembl.models import Idea, IdeaLink, LangString title = LangString.create(u"Lower government revenue", 'en') title.add_value(u'Moins de revenus pour le gouvernement', 'fr') i = Idea(title=title, discussion=discussion, description=LangString.create("Some definition of an idea", 'en')) test_session.add(i) l_11_111 = IdeaLink(source=subidea_1_1, target=i) test_session.add(l_11_111) test_session.flush() def fin(): print("finalizer subidea_1_1_1") test_session.delete(l_11_111) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def put_extract(request): """ Updating an Extract """ extract_id = request.matchdict['id'] user_id = authenticated_userid(request) 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) extract.owner_id = user_id or get_database_id("User", extract.owner_id) extract.order = updated_extract_data.get('order', extract.order) idea_id = updated_extract_data.get('idIdea', None) if idea_id: idea = Idea.get_instance(idea_id) if(idea.get_discussion_id() != extract.get_discussion_id()): 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 get_indexable_contents(session): from assembl.models import AgentProfile, Idea, Post from assembl.models.post import PublicationStates query = session.query(Idea ).filter(Idea.tombstone_condition() ).filter(Idea.hidden==False ).options( joinedload(Idea.title).joinedload("entries"), joinedload(Idea.synthesis_title).joinedload("entries"), joinedload(Idea.description).joinedload("entries") ) for idea in query: yield idea query = session.query(AgentProfile) for user in query: yield user AllPost = with_polymorphic(Post, '*') query = session.query(AllPost ).filter(AllPost.tombstone_condition() ).filter(AllPost.hidden==False ).filter(AllPost.publication_state == PublicationStates.PUBLISHED ).options( joinedload(AllPost.subject).joinedload("entries"), joinedload(AllPost.body).joinedload("entries") ) for post in query: for extract in post.extracts: yield extract yield post
def subidea_1_1_1_1_1(request, discussion, subidea_1_1_1_1, test_session): """An Idea fixture with a idealink to subidea_1_1_1_1 fixture - root_idea |-> subidea_1 |-> subidea_1_1 |-> subidea_1_1_1 |-> subidea_1_1_1_1 |-> subidea_1_1_1_1""" from assembl.models import Idea, IdeaLink i = Idea(short_title=u"Job loss", discussion=discussion, definition="Some definition of an idea") test_session.add(i) l_1111_11111 = IdeaLink(source=subidea_1_1_1_1, target=i) test_session.add(l_1111_11111) test_session.flush() def fin(): print "finalizer subidea_1_1_1_1_1_1" test_session.delete(l_1111_11111) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def subidea_1_1_1_1_2(request, discussion, subidea_1_1_1_1, test_session): """An Idea fixture with a idealink to subidea_1_1_1_1 fixture - root_idea |-> subidea_1 |-> subidea_1_1 |-> subidea_1_1_1 |-> subidea_1_1_1_1 |-> subidea_1_1_1_1 |-> subidea_1_1_1_2""" from assembl.models import Idea, IdeaLink, LangString i = Idea(title=LangString.create(u"Environmental program cuts", 'en'), discussion=discussion, description=LangString.create("Some definition of an idea", 'en')) test_session.add(i) l_1111_11112 = IdeaLink(source=subidea_1_1_1_1, target=i) test_session.add(l_1111_11112) test_session.flush() def fin(): print "finalizer subidea_1_1_1_1_2" test_session.delete(l_1111_11112) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def widget_view(request): # IF_OWNED not applicable for widgets... so far ctx = request.context user_id = authenticated_userid(request) or Everyone permissions = get_permissions(user_id, ctx.get_discussion_id()) check_permissions(ctx, user_id, permissions, CrudPermissions.READ) view = (request.matchdict or {}).get('view', None)\ or ctx.get_default_view() or 'default' json = ctx._instance.generic_json(view, user_id, permissions) # json['discussion'] = ... if user_id != Everyone: user = User.get(user_id) user_state = ctx._instance.get_user_state(user_id) json['user'] = user.generic_json(view, user_id, permissions) json['user_permissions'] = get_permissions( user_id, ctx._instance.get_discussion_id()) if user_state is not None: json['user_state'] = user_state target_id = request.GET.get('target', None) if target_id: idea = Idea.get_instance(target_id) if idea: json['target'] = idea.generic_json(view, user_id, permissions) else: return HTTPNotFound("No idea " + target_id) return json
def voting_widget_view(request): user_id = authenticated_userid(request) or Everyone ctx = request.context view = (request.matchdict or {}).get('view', None)\ or ctx.get_default_view() or 'default' widget = ctx._instance permissions = get_permissions(user_id, ctx.get_discussion_id()) json = widget.generic_json(view, user_id, permissions) #json['discussion'] = ... if user_id != Everyone: user = User.get(user_id) json['user'] = user.generic_json(view, user_id, permissions) json['user_permissions'] = get_permissions(user_id, widget.get_discussion_id()) user_state = widget.get_user_state(user_id) if user_state is not None: json['user_state'] = user_state target_id = request.GET.get('target', None) if target_id and Idea.get_database_id(target_id): json['user_votes_url'] = widget.get_user_votes_url(target_id) json['voting_urls'] = widget.get_voting_urls(target_id) json['criteria'] = [ idea.generic_json(view, user_id, permissions) for idea in widget.criteria ] return json
def add_idea_to_synthesis(request): """Add an idea to an ExplictSubgraphView""" ctx = request.context graph_view = ctx.parent_instance if isinstance(graph_view, Synthesis) and not graph_view.is_next_synthesis: raise HTTPBadRequest("Synthesis is published") content = request.json idea_id = content.get('@id', None) if not idea_id: raise HTTPBadRequest("Post an idea with its @id") idea = Idea.get_instance(idea_id) if not idea: raise HTTPNotFound("Unknown idea") link = SubGraphIdeaAssociation(idea=idea, sub_graph=graph_view) duplicate = link.find_duplicate(False) if duplicate: link.delete() return duplicate.idea.generic_json() graph_view.db.add(link) graph_view.db.expire(graph_view, ["idea_assocs"]) graph_view.send_to_changes() # special location return Response( json.dumps(idea.generic_json()), 201, content_type='application/json', location=request.url + "/" + str(idea.id))
def voting_widget_view(request): user_id = authenticated_userid(request) or Everyone ctx = request.context view = (request.matchdict or {}).get('view', None)\ or ctx.get_default_view() or 'default' widget = ctx._instance permissions = get_permissions( user_id, ctx.get_discussion_id()) json = widget.generic_json(view, user_id, permissions) #json['discussion'] = ... if user_id != Everyone: user = User.get(user_id) json['user'] = user.generic_json(view, user_id, permissions) json['user_permissions'] = get_permissions( user_id, widget.get_discussion_id()) user_state = widget.get_user_state(user_id) if user_state is not None: json['user_state'] = user_state target_id = request.GET.get('target', None) if target_id and Idea.get_database_id(target_id): json['user_votes_url'] = widget.get_user_votes_url(target_id) json['voting_urls'] = widget.get_voting_urls(target_id) json['criteria'] = [idea.generic_json(view, user_id, permissions) for idea in widget.criteria] return json
def post_draft_for_bright_mirror( request, test_session, discussion, moderator_user, bright_mirror): from assembl.models import Post, Idea, LangString, IdeaRelatedPostLink, PublicationStates from graphene.relay import Node idea_id = bright_mirror raw_id = int(Node.from_global_id(idea_id)[1]) idea = Idea.get(raw_id) p = Post( discussion=discussion, creator=moderator_user, subject=LangString.create(u"Draft"), body=LangString.create(u"A simple draft fiction"), type='post', publication_state=PublicationStates.DRAFT, message_id="*****@*****.**", creation_date = datetime.utcnow() - timedelta(days=7)) idc = IdeaRelatedPostLink( idea=idea, creator=moderator_user, content=p) test_session.add(p) test_session.add(idc) test_session.flush() def fin(): print "finalizer post_draft_for_bright_mirror" test_session.delete(p) test_session.delete(idc) test_session.flush() request.addfinalizer(fin) return p
def post_published_for_bright_mirror_participant( request, test_session, discussion, admin_user, participant1_user, bright_mirror): from assembl.models import Post, Idea, LangString, IdeaRelatedPostLink, PublicationStates from graphene.relay import Node idea_id = bright_mirror raw_id = int(Node.from_global_id(idea_id)[1]) idea = Idea.get(raw_id) p = Post( discussion=discussion, creator=participant1_user, subject=LangString.create(u"Published by participant"), body=LangString.create(u"A simple published fiction by participant"), type='post', publication_state=PublicationStates.PUBLISHED, message_id="*****@*****.**", creation_date = datetime.utcnow()) idc = IdeaRelatedPostLink( idea=idea, creator=admin_user, content=p) test_session.add(p) test_session.add(idc) test_session.flush() def fin(): print "finalizer post_published_for_bright_mirror" test_session.delete(p) test_session.delete(idc) test_session.flush() request.addfinalizer(fin) return p
def widget_view(request): # IF_OWNED not applicable for widgets... so far ctx = request.context user_id = authenticated_userid(request) or Everyone permissions = get_permissions( user_id, ctx.get_discussion_id()) check_permissions(ctx, user_id, permissions, CrudPermissions.READ) view = (request.matchdict or {}).get('view', None)\ or ctx.get_default_view() or 'default' json = ctx._instance.generic_json(view, user_id, permissions) # json['discussion'] = ... if user_id != Everyone: user = User.get(user_id) user_state = ctx._instance.get_user_state(user_id) json['user'] = user.generic_json(view, user_id, permissions) json['user_permissions'] = get_permissions( user_id, ctx._instance.get_discussion_id()) if user_state is not None: json['user_state'] = user_state target_id = request.GET.get('target', None) if target_id: idea = Idea.get_instance(target_id) if idea: json['target'] = idea.generic_json(view, user_id, permissions) else: return HTTPNotFound("No idea "+target_id) return json
def subidea_1_1_1_1_2_2(request, discussion, subidea_1_1_1_1_2, test_session): """An Idea fixture with a idealink to subidea_1_1_1_1_2 fixture:: root_idea |-> subidea_1 |-> subidea_1_1 |-> subidea_1_1_1 |-> subidea_1_1_1_1 |-> subidea_1_1_1_1 |-> subidea_1_1_1_2 |-> subidea_1_1_1_1_2_1 |-> subidea_1_1_1_1_2_2""" from assembl.models import Idea, IdeaLink, LangString i = Idea(title=LangString.create(u"Federal programs are ineffective", 'en'), discussion=discussion, description=LangString.create("Some definition of an idea", 'en')) test_session.add(i) l_11112_111122 = IdeaLink(source=subidea_1_1_1_1_2, target=i) test_session.add(l_11112_111122) test_session.flush() def fin(): print("finalizer subidea_1_1_1_1_2_2") test_session.delete(l_11112_111122) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def get_indexable_contents(session): from assembl.models import AgentProfile, Idea, Post from assembl.models.post import PublicationStates query = session.query(Idea).filter( Idea.tombstone_condition()).filter(Idea.hidden == False).options( joinedload(Idea.title).joinedload("entries"), joinedload(Idea.synthesis_title).joinedload("entries"), joinedload(Idea.description).joinedload("entries")) for idea in query: yield idea query = session.query(AgentProfile) for user in query: yield user AllPost = with_polymorphic(Post, '*') query = session.query(AllPost).filter( AllPost.tombstone_condition()).filter(AllPost.hidden == False).filter( AllPost.publication_state == PublicationStates.PUBLISHED).options( joinedload(AllPost.subject).joinedload("entries"), joinedload(AllPost.body).joinedload("entries")) for post in query: for extract in post.extracts: yield extract yield post
def creativity_session_widget_new_idea( request, test_session, discussion, subidea_1, creativity_session_widget, participant1_user): from assembl.models import (Idea, IdeaLink, GeneratedIdeaWidgetLink, IdeaProposalPost) i = Idea( discussion=discussion, short_title="generated idea") test_session.add(i) l_1_wi = IdeaLink(source=subidea_1, target=i) test_session.add(l_1_wi) l_w_wi = GeneratedIdeaWidgetLink( widget=creativity_session_widget, idea=i) ipp = IdeaProposalPost( proposes_idea=i, creator=participant1_user, discussion=discussion, message_id='proposal', subject=u"propose idea", body="") test_session.add(ipp) def fin(): print "finalizer creativity_session_widget_new_idea" test_session.delete(ipp) test_session.delete(l_w_wi) test_session.delete(l_1_wi) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def test_add_subidea_in_synthesis(discussion, test_app, synthesis_1, subidea_1_1, test_session): new_idea_r = test_app.post_json( '/data/Conversation/%d/views/%d/ideas/%d/children' % (discussion.id, synthesis_1.id, subidea_1_1.id), { "shortTitle": { "@type": "LangString", "entries": [{ "@type": "LangStringEntry", "@language": "en", "value": "New subidea" }] } }) assert new_idea_r.status_code == 201 link = new_idea_r.location new_idea = Idea.get_instance(link) assert new_idea db = discussion.db idea_link = db.query(IdeaLink).filter_by(target=new_idea, source=subidea_1_1).first() assert idea_link idea_assoc = db.query(SubGraphIdeaAssociation).filter_by( idea=new_idea, sub_graph=synthesis_1).first() assert idea_assoc idealink_assoc = db.query(SubGraphIdeaLinkAssociation).filter_by( sub_graph=synthesis_1, idea_link=idea_link).first() assert idealink_assoc idealink_assoc.delete() idea_assoc.delete() idea_link.delete() new_idea.delete() new_idea.db.commit()
def subidea_1( request, discussion, root_idea, test_session, ): """An Idea fixture with a idealink to root idea fixture - root_idea |-> subidea_1""" from assembl.models import Idea, IdeaLink, LangString i = Idea(title=LangString.create(u"Favor economic growth", 'en'), discussion=discussion, description=LangString.create("Some definition of an idea", 'en')) test_session.add(i) l_r_1 = IdeaLink(source=root_idea, target=i) test_session.add(l_r_1) test_session.flush() def fin(): print("finalizer subidea_1") test_session.delete(l_r_1) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def subidea_1_2_1(request, discussion, subidea_1_2, test_session): """An Idea fixture with a idealink to subidea_1 fixture:: root_idea |-> subidea_1 |-> subidea_1_2 |-> subidea_1_2_1""" from assembl.models import Idea, IdeaLink, LangString i = Idea(title=LangString.create(u"Bad for the environment", 'en'), discussion=discussion, description=LangString.create("Some definition of an idea", 'en')) test_session.add(i) l_12_121 = IdeaLink(source=subidea_1_2, target=i) test_session.add(l_12_121) test_session.flush() def fin(): print("finalizer subidea_1_2_1") test_session.delete(l_12_121) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def creativity_session_widget_new_idea(request, test_session, discussion, subidea_1, creativity_session_widget, participant1_user): """An Idea fixture with an bound ideaLink, GeneratedIdeaWidgetLink, and a IdeaProposalPost""" from assembl.models import (Idea, IdeaLink, GeneratedIdeaWidgetLink, IdeaProposalPost, LangString) i = Idea(discussion=discussion, title=LangString.create(u"generated idea", 'en')) test_session.add(i) l_1_wi = IdeaLink(source=subidea_1, target=i) test_session.add(l_1_wi) l_w_wi = GeneratedIdeaWidgetLink(widget=creativity_session_widget, idea=i) ipp = IdeaProposalPost(proposes_idea=i, creator=participant1_user, discussion=discussion, message_id='*****@*****.**', subject=LangString.create(u"propose idea"), body=LangString.EMPTY(test_session)) test_session.add(ipp) def fin(): print "finalizer creativity_session_widget_new_idea" test_session.delete(ipp) test_session.delete(l_w_wi) test_session.delete(l_1_wi) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def subidea_1_1_1_1(request, discussion, subidea_1_1_1, test_session): """An Idea fixture with a idealink to subidea_1_1_1 fixture:: root_idea |-> subidea_1 |-> subidea_1_1 |-> subidea_1_1_1 |->subidea_1_1_1_1""" from assembl.models import Idea, IdeaLink, LangString i = Idea(title=LangString.create(u"Austerity yields contraction", 'en'), discussion=discussion, description=LangString.create("Some definition of an idea", 'en')) test_session.add(i) l_111_1111 = IdeaLink(source=subidea_1_1_1, target=i) test_session.add(l_111_1111) test_session.flush() def fin(): print "finalizer subidea_1_1_1_1" test_session.delete(l_111_1111) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def add_idea_to_synthesis(request): """Add an idea to an ExplictSubgraphView""" ctx = request.context graph_view = ctx.parent_instance if isinstance(graph_view, Synthesis) and not graph_view.is_next_synthesis: raise HTTPBadRequest("Synthesis is published") content = request.json idea_id = content.get('@id', None) if not idea_id: raise HTTPBadRequest("Post an idea with its @id") idea = Idea.get_instance(idea_id) if not idea: raise HTTPNotFound("Unknown idea") link = SubGraphIdeaAssociation(idea=idea, sub_graph=graph_view) duplicate = link.find_duplicate(False) if duplicate: link.delete() return duplicate.idea.generic_json() graph_view.db.add(link) graph_view.db.expire(graph_view, ["idea_assocs"]) graph_view.send_to_changes() # special location return Response(json.dumps(idea.generic_json()), 201, content_type='application/json', location=request.url + "/" + str(idea.id))
def delete_idea(request): idea_id = request.matchdict['id'] idea = Idea.get_instance(idea_id) if not idea: raise HTTPNotFound("Idea with id '%s' not found." % idea_id) if isinstance(idea, RootIdea): raise HTTPBadRequest("Cannot delete root idea.") num_childrens = len(idea.children) if num_childrens > 0: raise HTTPBadRequest("Idea cannot be deleted because it still has %d child ideas." % num_childrens) num_extracts = len(idea.extracts) if num_extracts > 0: raise HTTPBadRequest("Idea cannot be deleted because it still has %d extracts." % num_extracts) db = Idea.db() db.delete(idea) request.response.status = HTTPNoContent.code return HTTPNoContent()
def post_ids_of(idea): related = text( Idea._get_related_posts_statement(), bindparams=[bindparam('root_idea_id', idea.id), bindparam('discussion_id', idea.discussion_id)] ).columns(column('post_id')).alias('related') post_ids = idea.db.query(Content.id).join( related, Content.id == related.c.post_id) return [x for (x,) in post_ids]
def create_idea(request): discussion_id = int(request.matchdict["discussion_id"]) session = Discussion.default_db discussion = session.query(Discussion).get(int(discussion_id)) idea_data = json.loads(request.body) new_idea = Idea(short_title=idea_data["shortTitle"], long_title=idea_data["longTitle"], discussion=discussion) session.add(new_idea) if idea_data["parentId"]: parent = Idea.get_instance(idea_data["parentId"]) else: parent = discussion.root_idea session.add(IdeaLink(source=parent, target=new_idea, order=idea_data.get("order", 0.0))) session.flush() return {"ok": True, "@id": new_idea.uri()}
def idea_connexions(request): # Returns the ideas this post is connected to. # This is a list of tuples of the form: # [idea, connexion_idea, connexion_post, extract?] # the idea is an ancestor or equal to connexion_idea # the connexion_post is an ancestor or equal to current post from assembl.models import Idea ctx = request.context post = ctx._instance return Idea.get_idea_ids_showing_post(post.id, True, True)
def mark_post_read(request): """Mark this post as un/read. Return the read post count for all affected ideas.""" discussion_id = int(request.matchdict['discussion_id']) discussion = Discussion.get_instance(discussion_id) post_id = request.matchdict['id'] post = Post.get_instance(post_id) if not post: raise HTTPNotFound("Post with id '%s' not found." % post_id) post_id = post.id user_id = authenticated_userid(request) if not user_id: raise HTTPUnauthorized() read_data = json.loads(request.body) db = discussion.db change = False with transaction.manager: if read_data.get('read', None) is False: view = db.query(ViewPost).filter_by(post_id=post_id, actor_id=user_id, tombstone_date=None).first() if view: change = True view.is_tombstone = True else: count = db.query(ViewPost).filter_by(post_id=post_id, actor_id=user_id, tombstone_date=None).count() if not count: change = True db.add(ViewPost(post=post, actor_id=user_id)) new_counts = [] if change: new_counts = Idea.idea_read_counts(discussion_id, post_id, user_id) return { "ok": True, "ideas": [{ "@id": Idea.uri_generic(idea_id), "num_read_posts": read_posts } for (idea_id, read_posts) in new_counts] }
def mark_post_read(request): discussion_id = int(request.matchdict["discussion_id"]) discussion = Discussion.get_instance(discussion_id) post_id = request.matchdict["id"] post = Post.get_instance(post_id) if not post: raise HTTPNotFound("Post with id '%s' not found." % post_id) post_id = post.id user_id = authenticated_userid(request) if not user_id: raise HTTPUnauthorized() read_data = json.loads(request.body) db = Discussion.db() change = False with transaction.manager: if read_data.get("read", None) is False: view = db.query(ViewPost).filter(ViewPost.post_id == post_id).filter(Action.actor_id == user_id).first() if view: change = True db.delete(view) else: count = db.query(ViewPost).filter(ViewPost.post_id == post_id).filter(Action.actor_id == user_id).count() if not count: change = True db.add(ViewPost(post=post, actor_id=user_id)) new_counts = [] if change: new_counts = Idea.idea_counts(discussion_id, post_id, user_id) return { "ok": True, "ideas": [ { "@id": Idea.uri_generic(idea_id), "@type": db.query(Idea).get(idea_id).external_typename(), "num_posts": total_posts, "num_read_posts": read_posts, } for (idea_id, total_posts, read_posts) in new_counts ], }
def post_to_vote_votables(request): ctx = request.context target_id = request.POST.get('id', None) idea = None if target_id: idea = Idea.get_instance(target_id) if not idea: raise HTTPNotFound widget = ctx.parent_instance widget.add_votable(idea) return HTTPOk() # Not sure this can be called a creation
def get_idea(request): idea_id = request.matchdict['id'] idea = Idea.get_instance(idea_id) view_def = request.GET.get('view') discussion_id = int(request.matchdict['discussion_id']) user_id = authenticated_userid(request) or Everyone permissions = get_permissions(user_id, discussion_id) if not idea: raise HTTPNotFound("Idea with id '%s' not found." % idea_id) return idea.generic_json(view_def, user_id, permissions)
def get_idea(request): idea_id = request.matchdict['id'] idea = Idea.get_instance(idea_id) view_def = request.GET.get('view') discussion_id = int(request.matchdict['discussion_id']) user_id = authenticated_userid(request) permissions = get_permissions(user_id, discussion_id) if not idea: raise HTTPNotFound("Idea with id '%s' not found." % idea_id) return idea.generic_json(view_def, user_id, permissions)
def get_idea(request): idea_id = request.matchdict['id'] idea = Idea.get_instance(idea_id) view_def = request.GET.get('view') if not idea: raise HTTPNotFound("Idea with id '%s' not found." % idea_id) if view_def: return idea.generic_json(view_def) else: return idea.generic_json()
def mark_post_read(request): """Mark this post as un/read. Return the read post count for all affected ideas.""" discussion_id = int(request.matchdict['discussion_id']) discussion = Discussion.get_instance(discussion_id) post_id = request.matchdict['id'] post = Post.get_instance(post_id) if not post: raise HTTPNotFound("Post with id '%s' not found." % post_id) post_id = post.id user_id = request.authenticated_userid if not user_id: raise HTTPUnauthorized() read_data = json.loads(request.body) db = discussion.db change = False with transaction.manager: if read_data.get('read', None) is False: view = db.query(ViewPost).filter_by( post_id=post_id, actor_id=user_id, tombstone_date=None).first() if view: change = True view.is_tombstone = True else: count = db.query(ViewPost).filter_by( post_id=post_id, actor_id=user_id, tombstone_date=None).count() if not count: change = True db.add(ViewPost(post=post, actor_id=user_id)) new_counts = [] if change: new_counts = Idea.idea_read_counts(discussion_id, post_id, user_id) return { "ok": True, "ideas": [ {"@id": Idea.uri_generic(idea_id), "num_read_posts": read_posts } for (idea_id, read_posts) in new_counts] }
def create_idea(request): discussion_id = int(request.matchdict['discussion_id']) session = Discussion.db() discussion = session.query(Discussion).get(int(discussion_id)) idea_data = json.loads(request.body) new_idea = Idea( short_title=idea_data['shortTitle'], long_title=idea_data['longTitle'], discussion=discussion, ) session.add(new_idea) if idea_data['parentId']: parent = Idea.get_instance(idea_data['parentId']) else: parent = discussion.root_idea session.add(IdeaLink(source=parent, target=new_idea, order=idea_data.get('order', 0.0))) session.flush() return {'ok': True, '@id': new_idea.uri()}
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 get_idea_extracts(request): idea_id = request.matchdict["id"] idea = Idea.get_instance(idea_id) view_def = request.GET.get("view") or "default" discussion_id = int(request.matchdict["discussion_id"]) user_id = authenticated_userid(request) or Everyone permissions = get_permissions(user_id, discussion_id) if not idea: raise HTTPNotFound("Idea with id '%s' not found." % idea_id) extracts = Extract.default_db.query(Extract).filter(Extract.idea_id == idea.id).order_by(Extract.order.desc()) return [extract.generic_json(view_def, user_id, permissions) for extract in extracts]
def create_idea(request): discussion_id = int(request.matchdict['discussion_id']) session = Discussion.default_db discussion = session.query(Discussion).get(int(discussion_id)) user_id = request.authenticated_userid permissions = get_permissions(user_id, discussion.id) idea_data = json.loads(request.body) kwargs = {"discussion": discussion} for key, attr_name in langstring_fields.iteritems(): if key in idea_data: ls_data = idea_data[key] if ls_data is None: continue assert isinstance(ls_data, dict) current = LangString.create_from_json(ls_data, user_id, permissions=permissions) kwargs[attr_name] = current new_idea = Idea(**kwargs) session.add(new_idea) if idea_data['parentId']: parent = Idea.get_instance(idea_data['parentId']) else: parent = discussion.root_idea session.add( IdeaLink(source=parent, target=new_idea, order=idea_data.get('order', 0.0))) session.flush() return {'ok': True, '@id': new_idea.uri()}
def subidea_1_1_1(request, discussion, subidea_1_1, test_session): from assembl.models import Idea, IdeaLink i = Idea(short_title="idea 1.1.1", discussion=discussion) test_session.add(i) l_11_111 = IdeaLink(source=subidea_1_1, target=i) test_session.add(l_11_111) test_session.flush() def fin(): print "finalizer subidea_1_1_1" test_session.delete(l_11_111) test_session.delete(i) test_session.flush() request.addfinalizer(fin) return i
def get_idea_extracts(request): idea_id = request.matchdict['id'] idea = Idea.get_instance(idea_id) view_def = request.GET.get('view') if not idea: raise HTTPNotFound("Idea with id '%s' not found." % idea_id) extracts = Extract.db.query(Extract).filter( Extract.idea_id == idea.id ).order_by(Extract.order.desc()) if view_def: return [extract.generic_json(view_def) for extract in extracts] else: return [extract.serializable() for extract in extracts]
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 test_add_subidea_in_synthesis( discussion, test_app, synthesis_1, subidea_1_1, test_session): new_idea_r = test_app.post( '/data/Discussion/%d/views/%d/ideas/%d/children' % ( discussion.id, synthesis_1.id, subidea_1_1.id), {"short_title": "New subidea"}) assert new_idea_r.status_code == 201 link = new_idea_r.location new_idea = Idea.get_instance(link) assert new_idea db = discussion.db idea_link = db.query(IdeaLink).filter_by( target=new_idea, source=subidea_1_1).first() assert idea_link idea_assoc = db.query(SubGraphIdeaAssociation).filter_by( idea=new_idea, sub_graph=synthesis_1).first() assert idea_assoc idealink_assoc = db.query(SubGraphIdeaLinkAssociation).filter_by( sub_graph=synthesis_1, idea_link=idea_link).first() assert idealink_assoc
def delete_idea(request): idea_id = request.matchdict['id'] idea = Idea.get_instance(idea_id) if not idea: raise HTTPNotFound("Idea with id '%s' not found." % idea_id) if isinstance(idea, RootIdea): raise HTTPBadRequest("Cannot delete root idea.") num_childrens = len(idea.children) if num_childrens > 0: raise HTTPBadRequest("Idea cannot be deleted because it still has %d child ideas." % num_childrens) num_extracts = len(idea.extracts) if num_extracts > 0: raise HTTPBadRequest("Idea cannot be deleted because it still has %d extracts." % num_extracts) for link in idea.source_links: link.is_tombstone = True idea.is_tombstone = True # Maybe return tombstone() ? request.response.status = HTTPNoContent.code return HTTPNoContent()
def get_idea_extracts(request): idea_id = request.matchdict['id'] idea = Idea.get_instance(idea_id) view_def = request.GET.get('view') discussion_id = int(request.matchdict['discussion_id']) user_id = authenticated_userid(request) permissions = get_permissions(user_id, discussion_id) if not idea: raise HTTPNotFound("Idea with id '%s' not found." % idea_id) extracts = Extract.db.query(Extract).filter( Extract.idea_id == idea.id ).order_by(Extract.order.desc()) if view_def: return [extract.generic_json(view_def, user_id, permissions) for extract in extracts] else: return [extract.serializable() for extract in extracts]
def get_posts(request): """ Query interface on posts Filters have two forms: only_*, is for filters that cannot be reversed (ex: only_synthesis) is_*, is for filters that can be reversed (ex:is_unread=true returns only unread order can be chronological, reverse_chronological message, is_unread=false returns only read messages) """ localizer = request.localizer discussion_id = int(request.matchdict['discussion_id']) discussion = Discussion.get(int(discussion_id)) if not discussion: raise HTTPNotFound(localizer.translate( _("No discussion found with id=%s")) % discussion_id) discussion.import_from_sources() user_id = authenticated_userid(request) or Everyone permissions = get_permissions(user_id, discussion_id) DEFAULT_PAGE_SIZE = 25 page_size = DEFAULT_PAGE_SIZE filter_names = [ filter_name for filter_name \ in request.GET.getone('filters').split(',') \ if filter_name ] if request.GET.get('filters') else [] try: page = int(request.GET.getone('page')) except (ValueError, KeyError): page = 1 text_search = request.GET.get('text_search', None) order = request.GET.get('order') if order == None: order = 'chronological' assert order in ('chronological', 'reverse_chronological', 'score') if order == 'score': assert text_search is not None if page < 1: page = 1 root_post_id = request.GET.getall('root_post_id') if root_post_id: root_post_id = get_database_id("Post", root_post_id[0]) family_post_id = request.GET.getall('family_post_id') if family_post_id: family_post_id = get_database_id("Post", family_post_id[0]) root_idea_id = request.GET.getall('root_idea_id') if root_idea_id: root_idea_id = get_database_id("Idea", root_idea_id[0]) ids = request.GET.getall('ids[]') if ids: ids = [get_database_id("Post", id) for id in ids] view_def = request.GET.get('view') or 'default' only_synthesis = request.GET.get('only_synthesis') post_author_id = request.GET.get('post_author') if post_author_id: post_author_id = get_database_id("AgentProfile", post_author_id) assert AgentProfile.get(post_author_id), "Unable to find agent profile with id " + post_author_id post_replies_to = request.GET.get('post_replies_to') if post_replies_to: post_replies_to = get_database_id("AgentProfile", post_replies_to) assert AgentProfile.get(post_replies_to), "Unable to find agent profile with id " + post_replies_to posted_after_date = request.GET.get('posted_after_date') posted_before_date = request.GET.get('posted_before_date') PostClass = SynthesisPost if only_synthesis == "true" else Post ideaContentLinkQuery = discussion.db.query( PostClass.id, PostClass.idea_content_links_above_post) if order == 'score': posts = discussion.db.query(PostClass, Content.body_text_index.score_name) else: posts = discussion.db.query(PostClass) posts = posts.filter( PostClass.discussion_id == discussion_id, ) ideaContentLinkQuery = ideaContentLinkQuery.filter( PostClass.discussion_id == discussion_id) ##no_of_posts_to_discussion = posts.count() post_data = [] only_orphan = request.GET.get('only_orphan') if only_orphan == "true": if root_idea_id: raise HTTPBadRequest(localizer.translate( _("Getting orphan posts of a specific idea isn't supported."))) orphans = text(Idea._get_orphan_posts_statement(), bindparams=[bindparam('discussion_id', discussion_id)] ).columns(column('post_id')).alias('orphans') posts = posts.join(orphans, PostClass.id==orphans.c.post_id) ideaContentLinkQuery = ideaContentLinkQuery.join( orphans, PostClass.id==orphans.c.post_id) elif only_orphan == "false": raise HTTPBadRequest(localizer.translate( _("Getting non-orphan posts isn't supported."))) # "true" means hidden only, "false" (default) means visible only. "any" means both. hidden = request.GET.get('hidden_messages', "false") if hidden != 'any': posts = posts.filter(PostClass.hidden==asbool(hidden)) ideaContentLinkQuery = ideaContentLinkQuery.filter( PostClass.hidden==asbool(hidden)) if root_idea_id: related = text(Idea._get_related_posts_statement(), bindparams=[bindparam('root_idea_id', root_idea_id), bindparam('discussion_id', discussion_id)] ).columns(column('post_id')).alias('related') #Virtuoso bug: This should work... #posts = posts.join(related, PostClass.id==related.c.post_id) posts = posts.join(related, PostClass.id == related.c.post_id) ideaContentLinkQuery = ideaContentLinkQuery.join( related, PostClass.id == related.c.post_id) if root_post_id: root_post = Post.get(root_post_id) posts = posts.filter( (Post.ancestry.like( root_post.ancestry + cast(root_post.id, String) + ',%' )) | (PostClass.id==root_post.id) ) elif family_post_id: root_post = Post.get(family_post_id) ancestor_ids = root_post.ancestor_ids() posts = posts.filter( (Post.ancestry.like( root_post.ancestry + cast(root_post.id, String) + ',%' )) | (PostClass.id==root_post.id) | (PostClass.id.in_(ancestor_ids)) ) ideaContentLinkQuery = ideaContentLinkQuery.filter( (Post.ancestry.like( root_post.ancestry + cast(root_post.id, String) + ',%' )) | (PostClass.id==root_post.id) | (PostClass.id.in_(ancestor_ids)) ) else: root_post = None if ids: posts = posts.filter(Post.id.in_(ids)) ideaContentLinkQuery = ideaContentLinkQuery.filter(Post.id.in_(ids)) if posted_after_date: posted_after_date = parse_datetime(posted_after_date) if posted_after_date: posts = posts.filter(PostClass.creation_date >= posted_after_date) ideaContentLinkQuery = ideaContentLinkQuery.filter( PostClass.creation_date >= posted_after_date) #Maybe we should do something if the date is invalid. benoitg if posted_before_date: posted_before_date = parse_datetime(posted_before_date) if posted_before_date: posts = posts.filter(PostClass.creation_date <= posted_before_date) ideaContentLinkQuery = posts.filter( ideaContentLinkQuery.creation_date <= posted_before_date) #Maybe we should do something if the date is invalid. benoitg if post_author_id: posts = posts.filter(PostClass.creator_id == post_author_id) ideaContentLinkQuery = ideaContentLinkQuery.filter( PostClass.creator_id == post_author_id) if post_replies_to: parent_alias = aliased(PostClass) posts = posts.join(parent_alias, PostClass.parent) posts = posts.filter(parent_alias.creator_id == post_replies_to) ideaContentLinkQuery = ideaContentLinkQuery.join( parent_alias, PostClass.parent) ideaContentLinkQuery = ideaContentLinkQuery.filter( parent_alias.creator_id == post_replies_to) # Post read/unread management is_unread = request.GET.get('is_unread') translations = None if user_id != Everyone: # This is horrible, but the join creates complex subqueries that # virtuoso cannot decode properly. read_posts = {v.post_id for v in discussion.db.query( ViewPost).filter( ViewPost.tombstone_condition(), ViewPost.actor_id == user_id, *ViewPost.get_discussion_conditions(discussion_id))} liked_posts = {l.post_id: l.id for l in discussion.db.query( LikedPost).filter( LikedPost.tombstone_condition(), LikedPost.actor_id == user_id, *LikedPost.get_discussion_conditions(discussion_id))} if is_unread != None: posts = posts.outerjoin( ViewPost, and_( ViewPost.actor_id==user_id, ViewPost.post_id==PostClass.id, ViewPost.tombstone_date == None)) if is_unread == "true": posts = posts.filter(ViewPost.id == None) elif is_unread == "false": posts = posts.filter(ViewPost.id != None) user = AgentProfile.get(user_id) service = discussion.translation_service() if service: translations = user_pref_as_translation_table(user, service) else: #If there is no user_id, all posts are always unread if is_unread == "false": raise HTTPBadRequest(localizer.translate( _("You must be logged in to view which posts are read"))) if text_search is not None: # another Virtuoso bug: offband kills score. but it helps speed. offband = () if (order == 'score') else None posts = posts.filter(Post.body_text_index.contains( text_search.encode('utf-8'), offband=offband)) ideaContentLinkQuery = ideaContentLinkQuery.filter( Post.body_text_index.contains( text_search.encode('utf-8'), offband=offband)) # posts = posts.options(contains_eager(Post.source)) # Horrible hack... But useful for structure load if view_def == 'id_only': pass # posts = posts.options(defer(Post.body)) else: posts = posts.options( # undefer(Post.idea_content_links_above_post), joinedload_all(Post.creator), joinedload_all(Post.extracts), joinedload_all(Post.widget_idea_links), joinedload_all(SynthesisPost.publishes_synthesis), subqueryload_all(Post.attachments)) if len(discussion.discussion_locales) > 1: posts = posts.options(*Content.subqueryload_options()) else: posts = posts.options(*Content.joinedload_options()) ideaContentLinkCache = dict(ideaContentLinkQuery.all()) if order == 'chronological': posts = posts.order_by(Content.creation_date) elif order == 'reverse_chronological': posts = posts.order_by(Content.creation_date.desc()) elif order == 'score': posts = posts.order_by(Content.body_text_index.score_name.desc()) else: posts = posts.order_by(Content.id) print str(posts) no_of_posts = 0 no_of_posts_viewed_by_user = 0 for query_result in posts: score, viewpost, likedpost = None, None, None if not isinstance(query_result, (list, tuple)): query_result = [query_result] post = query_result[0] if user_id != Everyone: viewpost = post.id in read_posts likedpost = liked_posts.get(post.id, None) if view_def != "id_only": translate_content( post, translation_table=translations, service=service) no_of_posts += 1 serializable_post = post.generic_json( view_def, user_id, permissions) or {} if order == 'score': score = query_result[1] serializable_post['score'] = score if viewpost: serializable_post['read'] = True no_of_posts_viewed_by_user += 1 elif user_id != Everyone and root_post is not None and root_post.id == post.id: # Mark post read, we requested it explicitely viewed_post = ViewPost( actor_id=user_id, post=root_post ) discussion.db.add(viewed_post) serializable_post['read'] = True else: serializable_post['read'] = False # serializable_post['liked'] = likedpost.uri() if likedpost else False serializable_post['liked'] = ( LikedPost.uri_generic(likedpost) if likedpost else False) if view_def != "id_only": serializable_post['indirect_idea_content_links'] = ( post.indirect_idea_content_links_with_cache( ideaContentLinkCache.get(post.id, None))) post_data.append(serializable_post) # Benoitg: For now, this completely garbles threading without intelligent #handling of pagination. Disabling #posts = posts.limit(page_size).offset(data['startIndex']-1) # This code isn't up to date. If limiting the query by page, we need to # calculate the counts with a separate query to have the right number of # results #no_of_messages_viewed_by_user = discussion.db.query(ViewPost).join( # Post #).filter( # Post.discussion_id == discussion_id, # ViewPost.actor_id == user_id, #).count() if user_id else 0 data = {} data["page"] = page data["unread"] = no_of_posts - no_of_posts_viewed_by_user data["total"] = no_of_posts data["maxPage"] = max(1, ceil(float(data["total"])/page_size)) #TODO: Check if we want 1 based index in the api data["startIndex"] = (page_size * page) - (page_size-1) if data["page"] == data["maxPage"]: data["endIndex"] = data["total"] else: data["endIndex"] = data["startIndex"] + (page_size-1) data["posts"] = post_data return data
def create_post(request): """ We use post, not put, because we don't know the id of the post """ localizer = request.localizer request_body = json.loads(request.body) user_id = authenticated_userid(request) if not user_id: raise HTTPUnauthorized() user = Post.default_db.query(User).filter_by(id=user_id).one() body = request_body.get('body', None) html = request_body.get('html', None) # BG: Is this used now? I cannot see it. reply_id = request_body.get('reply_id', None) idea_id = request_body.get('idea_id', None) subject = request_body.get('subject', None) publishes_synthesis_id = request_body.get('publishes_synthesis_id', None) if not body and not publishes_synthesis_id: # Should we allow empty messages otherwise? raise HTTPBadRequest(localizer.translate( _("Your message is empty"))) if reply_id: in_reply_to_post = Post.get_instance(reply_id) else: in_reply_to_post = None if idea_id: in_reply_to_idea = Idea.get_instance(idea_id) else: in_reply_to_idea = None discussion_id = int(request.matchdict['discussion_id']) discussion = Discussion.get_instance(discussion_id) if not discussion: raise HTTPNotFound(localizer.translate(_( "No discussion found with id=%s")) % (discussion_id,) ) ctx = DummyContext({Discussion: discussion}) if html: log.warning("Still using html") # how to guess locale in this case? body = LangString.create(html) elif body: body = LangString.create_from_json( body, context=ctx, user_id=user_id) else: body = LangString.EMPTY(discussion.db) if subject: subject = LangString.create_from_json( subject, context=ctx, user_id=user_id) else: # print(in_reply_to_post.subject, discussion.topic) if in_reply_to_post: subject = (in_reply_to_post.get_title().first_original().value if in_reply_to_post.get_title() else '') elif in_reply_to_idea: # TODO: THis should use a cascade like the frontend subject = (in_reply_to_idea.short_title if in_reply_to_idea.short_title else '') else: subject = discussion.topic if discussion.topic else '' # print subject if subject is not None and len(subject): new_subject = "Re: " + restrip_pat.sub('', subject).strip() if (in_reply_to_post and new_subject == subject and in_reply_to_post.get_title()): # reuse subject and translations subject = in_reply_to_post.get_title() else: # how to guess locale in this case? subject = LangString.create(new_subject) else: raven_client = get_raven_client() if raven_client: raven_client.captureMessage( "A message is about to be written to the database with an " "empty subject. This is not supposed to happen.") subject = LangString.EMPTY(discussion.db) post_constructor_args = { 'discussion': discussion, 'creator_id': user_id, 'subject': subject, 'body': body } if publishes_synthesis_id: published_synthesis = Synthesis.get_instance(publishes_synthesis_id) post_constructor_args['publishes_synthesis'] = published_synthesis new_post = SynthesisPost(**post_constructor_args) else: new_post = AssemblPost(**post_constructor_args) discussion.db.add(new_post) discussion.db.flush() if in_reply_to_post: new_post.set_parent(in_reply_to_post) if in_reply_to_idea: idea_post_link = IdeaRelatedPostLink( creator_id=user_id, content=new_post, idea=in_reply_to_idea ) discussion.db.add(idea_post_link) idea = in_reply_to_idea while idea: idea.send_to_changes() parents = idea.get_parents() idea = next(iter(parents)) if parents else None else: discussion.root_idea.send_to_changes() for source in discussion.sources: if 'send_post' in dir(source): source.send_post(new_post) permissions = get_permissions(user_id, discussion_id) return new_post.generic_json('default', user_id, permissions)
def create_post(request): """ We use post, not put, because we don't know the id of the post """ localizer = request.localizer request_body = json.loads(request.body) user_id = authenticated_userid(request) if not user_id: raise HTTPUnauthorized() user = Post.default_db.query(User).filter_by(id=user_id).one() message = request_body.get('message', None) html = request_body.get('html', None) reply_id = request_body.get('reply_id', None) idea_id = request_body.get('idea_id', None) subject = request_body.get('subject', None) publishes_synthesis_id = request_body.get('publishes_synthesis_id', None) if not message: raise HTTPBadRequest(localizer.translate( _("Your message is empty"))) if reply_id: in_reply_to_post = Post.get_instance(reply_id) else: in_reply_to_post = None if idea_id: in_reply_to_idea = Idea.get_instance(idea_id) else: in_reply_to_idea = None discussion_id = int(request.matchdict['discussion_id']) discussion = Discussion.get_instance(discussion_id) if not discussion: raise HTTPNotFound( localizer.translate(_("No discussion found with id=%s" % discussion_id)) ) if subject: subject = subject else: #print(in_reply_to_post.subject, discussion.topic) if in_reply_to_post: subject = in_reply_to_post.get_title() if in_reply_to_post.get_title() else '' elif in_reply_to_idea: #TODO: THis should use a cascade like the frontend subject = in_reply_to_idea.short_title if in_reply_to_idea.short_title else '' else: subject = discussion.topic if discussion.topic else '' #print subject subject = "Re: " + restrip_pat.sub('', subject) post_constructor_args = { 'discussion': discussion, 'creator_id': user_id, 'subject': subject, 'body': html if html else message } if publishes_synthesis_id: published_synthesis = Synthesis.get_instance(publishes_synthesis_id) post_constructor_args['publishes_synthesis'] = published_synthesis new_post = SynthesisPost(**post_constructor_args) else: new_post = AssemblPost(**post_constructor_args) discussion.db.add(new_post) discussion.db.flush() if in_reply_to_post: new_post.set_parent(in_reply_to_post) if in_reply_to_idea: idea_post_link = IdeaRelatedPostLink( creator_id=user_id, content=new_post, idea=in_reply_to_idea ) discussion.db.add(idea_post_link) idea = in_reply_to_idea while idea: idea.send_to_changes() parents = idea.get_parents() idea = next(iter(parents)) if parents else None else: discussion.root_idea.send_to_changes() for source in discussion.sources: if 'send_post' in dir(source): source.send_post(new_post) permissions = get_permissions(user_id, discussion_id) return new_post.generic_json('default', user_id, permissions)
def save_idea(request): discussion_id = int(request.matchdict['discussion_id']) idea_id = request.matchdict['id'] idea_data = json.loads(request.body) #Idea.db.execute('set transaction isolation level read committed') # Special items in TOC, like unsorted posts. if idea_id in ['orphan_posts']: return {'ok': False, 'id': Idea.uri_generic(idea_id)} idea = Idea.get_instance(idea_id) if not idea: raise HTTPNotFound("No such idea: %s" % (idea_id)) if isinstance(idea, RootIdea): raise HTTPBadRequest("Cannot edit root idea.") discussion = Discussion.get(int(discussion_id)) if not discussion: raise HTTPNotFound("Discussion with id '%s' not found." % discussion_id) if(idea.discussion_id != discussion.id): raise HTTPBadRequest( "Idea from discussion %s cannot saved from different discussion (%s)." % (idea.discussion_id,discussion.id )) if 'shortTitle' in idea_data: idea.short_title = idea_data['shortTitle'] if 'longTitle' in idea_data: idea.long_title = idea_data['longTitle'] if 'definition' in idea_data: idea.definition = idea_data['definition'] if 'parentId' in idea_data and idea_data['parentId'] is not None: # TODO: Make sure this is sent as a list! parent = Idea.get_instance(idea_data['parentId']) # calculate it early to maximize contention. prev_ancestors = parent.get_all_ancestors() new_ancestors = set() order = idea_data.get('order', 0.0) if not parent: raise HTTPNotFound("Missing parentId %s" % (idea_data['parentId'])) current_parent = None for parent_link in idea.source_links: pl_ancestors = parent_link.source.get_all_ancestors() new_ancestors.update(pl_ancestors) if parent_link.source != parent: parent_link.is_tombstone=True Idea.db.expire(idea, ['source_links']) for ancestor in pl_ancestors: if ancestor in prev_ancestors: break ancestor.send_to_changes() else: parent_link.order = order current_parent = parent_link Idea.db.expire(parent_link.source, ['target_links']) parent_link.source.send_to_changes() if current_parent is None: link = IdeaLink(source=parent, target=idea, order=order) Idea.db.add(link) # None of these 3 calls should be necessary, but they do help with # the parents being available (the "empty parent" bug). # The root cause is somewhere IdeaLink, or in sqlalchemy proper # but I can't seem to find it - benoitg - 2014-05-27 Idea.db.flush() Idea.db.expire(parent, ['target_links']) Idea.db.expire(idea, ['source_links']) parent.send_to_changes() for ancestor in prev_ancestors: if ancestor in new_ancestors: break ancestor.send_to_changes() idea.is_in_next_synthesis = idea_data.get('inNextSynthesis', False) idea.send_to_changes() return {'ok': True, 'id': idea.uri() }