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 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 _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 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 autocomplete(request): discussion = request.context.get_instance_of_class(Discussion) keywords = request.GET.get('q') if not keywords: raise HTTPBadRequest("please specify search terms (q)") locales = request.GET.getall('locale') user_prefs = LanguagePreferenceCollection.getCurrent() if not locales: locales = user_prefs.known_languages() if not set(locales).intersection(discussion.discussion_locales): locales.extend(discussion.discussion_locales) include_description = bool(request.GET.get('description')) match_lse = aliased(LangStringEntry) title_ls = aliased(LangString) query = Idea.default_db.query(Idea.id, title_ls, match_lse.langstring_id, match_lse.locale).join( title_ls, title_ls.id == Idea.title_id).filter( Idea.discussion_id == discussion.id) limit = int(request.GET.get('limit') or 5) columns = [Idea.title_id] if include_description: columns.append(Idea.description_id) query, rank = add_text_search(query, columns, keywords.split(), locales, True, match_lse) query = query.order_by(rank.desc()).limit(limit).all() results = [] if not query: return results for (idea_id, title, ls_id, lse_locale, rank) in query: title_entry = title.best_lang(user_prefs, False) r = { 'id': Idea.uri_generic(idea_id), 'title_locale': title_entry.locale, 'match_locale': lse_locale, 'text': title_entry.value } if include_description: r['match_field'] = 'shortTitle' if ls_id == title.id else 'definition' results.append(r) return {'results': results}
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 save_idea(request): """Update this idea. In case the ``parentId`` is changed, handle all ``IdeaLink`` changes and send relevant ideas on the socket.""" discussion_id = int(request.matchdict['discussion_id']) user_id = request.authenticated_userid permissions = get_permissions(user_id, discussion_id) idea_id = request.matchdict['id'] idea_data = json.loads(request.body) #Idea.default_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 )) simple_fields = { 'message_view_override': 'message_view_override', 'messages_in_parent': 'messages_in_parent', } for key, attr_name in simple_fields.iteritems(): if key in idea_data: setattr(idea, attr_name, idea_data[key]) for key, attr_name in langstring_fields.iteritems(): if key in idea_data: current = getattr(idea, attr_name) ls_data = idea_data[key] # TODO: handle legacy string instance? assert isinstance(ls_data, (dict, NoneType)) if current: if ls_data: current.update_from_json( ls_data, user_id, permissions=permissions) else: current.delete() elif ls_data: current = LangString.create_from_json( ls_data, user_id, permissions=permissions) setattr(idea, attr_name, current) 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']) if not parent: raise HTTPNotFound("Missing parentId %s" % (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) for parent_link in idea.source_links: # still assuming there's only one. pl_parent = parent_link.source pl_ancestors = pl_parent.get_all_ancestors() new_ancestors.update(pl_ancestors) if parent_link.source != parent: parent_link.copy(True) parent_link.source = parent parent.db.expire(parent, ['target_links']) parent.db.expire(pl_parent, ['target_links']) for ancestor in pl_ancestors: if ancestor in prev_ancestors: break ancestor.send_to_changes() for ancestor in prev_ancestors: if ancestor in new_ancestors: break ancestor.send_to_changes() parent_link.order = order parent_link.db.expire(parent_link.source, ['target_links']) parent_link.source.send_to_changes() parent_link.db.flush() idea.send_to_changes() return {'ok': True, 'id': idea.uri() }
def save_idea(request): discussion_id = 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(id=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 )) idea.short_title = idea_data['shortTitle'] idea.long_title = idea_data['longTitle'] 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. ancestors = parent.get_all_ancestors() 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() if parent_link.source != parent: parent_link.is_tombstone=True else: parent_link.order = order current_parent = parent_link Idea.db.expire(parent_link.source, ['target_links']) parent_link.source.send_to_changes() for ancestor in pl_ancestors: ancestor.send_to_changes() if current_parent is None: link = IdeaLink(source=parent, target=idea, order=order) idea.source_links.append(link) Idea.db.expire(parent, ['target_links']) parent.send_to_changes() for ancestor in ancestors: ancestor.send_to_changes() Idea.db.expire(idea, ['source_links']) next_synthesis = discussion.get_next_synthesis() if idea_data['inNextSynthesis']: if idea not in next_synthesis.ideas: next_synthesis.ideas.append(idea) else: if idea in next_synthesis.ideas: next_synthesis.ideas.remove(idea) idea.send_to_changes() return {'ok': True, 'id': idea.uri() }
def save_idea(request): discussion_id = int(request.matchdict['discussion_id']) idea_id = request.matchdict['id'] idea_data = json.loads(request.body) #Idea.default_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'])) for parent_link in idea.source_links: # still assuming there's only one. pl_parent = parent_link.source pl_ancestors = pl_parent.get_all_ancestors() new_ancestors.update(pl_ancestors) if parent_link.source != parent: parent_link.copy(True) parent_link.source = parent parent.db.expire(parent, ['target_links']) parent.db.expire(pl_parent, ['target_links']) for ancestor in pl_ancestors: if ancestor in prev_ancestors: break ancestor.send_to_changes() for ancestor in prev_ancestors: if ancestor in new_ancestors: break ancestor.send_to_changes() parent_link.order = order parent_link.db.expire(parent_link.source, ['target_links']) parent_link.source.send_to_changes() parent_link.db.flush() idea.is_in_next_synthesis = idea_data.get('inNextSynthesis', False) idea.send_to_changes() return {'ok': True, 'id': idea.uri() }
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
def save_idea(request): """Update this idea. In case the ``parentId`` is changed, handle all ``IdeaLink`` changes and send relevant ideas on the socket.""" discussion = request.context user_id = authenticated_userid(request) permissions = request.permissions idea_id = request.matchdict['id'] idea_data = json.loads(request.body) # Idea.default_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) db = idea.db if not idea: raise HTTPNotFound("No such idea: %s" % (idea_id)) if isinstance(idea, RootIdea): raise HTTPBadRequest("Cannot edit root idea.") if (idea.discussion_id != discussion.id): raise HTTPBadRequest( "Idea from discussion %s cannot be saved from different discussion (%s)." % (idea.discussion_id, discussion.id)) context = idea.get_instance_context(request=request) for key, attr_name in langstring_fields.items(): if key in idea_data: current = getattr(idea, attr_name) ls_data = idea_data[key] # TODO: handle legacy string instance? subcontext = idea.get_collection_context(key, context) if current: if ls_data: current.update_from_json(ls_data, context=subcontext, permissions=permissions) else: current.delete() elif ls_data: current = LangString.create_from_json(ls_data, context=subcontext) setattr(idea, attr_name, current._instance) new_parent_id = idea_data.get('parentId', None) if new_parent_id: # TODO: Make sure this is sent as a list! # Actually, use embedded links to do this properly... new_parent_ids = {new_parent_id} old_parent_ids = { Idea.uri_generic(l.source_id) for l in idea.source_links } if new_parent_ids != old_parent_ids: added_parent_ids = new_parent_ids - old_parent_ids removed_parent_ids = old_parent_ids - new_parent_ids added_parents = [Idea.get_instance(id) for id in added_parent_ids] current_parents = idea.get_parents() removed_parents = [ p for p in current_parents if p.uri() in removed_parent_ids ] if None in added_parents: missing = [ id for id in added_parent_ids if not Idea.get_instance(id) ] raise HTTPNotFound("Missing parentId %s" % (','.join(missing))) if not idea.has_permission_req(P_ASSOCIATE_IDEA): raise HTTPUnauthorized("Cannot associate idea " + idea.uri()) for parent in added_parents + removed_parents: if not parent.has_permission_req(P_ASSOCIATE_IDEA): raise HTTPUnauthorized("Cannot associate parent idea " + idea.uri()) old_ancestors = set() new_ancestors = set() for parent in current_parents: old_ancestors.add(parent) old_ancestors.update(parent.get_all_ancestors()) kill_links = { l for l in idea.source_links if Idea.uri_generic(l.source_id) in removed_parent_ids } order = idea_data.get('order', 0.0) for parent in added_parents: if kill_links: link = kill_links.pop() db.expire(link.source, ['target_links']) link.copy(True) link.order = order link.source = parent else: link = IdeaLink(source=source, target=idea, order=order) db.add(link) db.expire(parent, ['target_links']) order += 1.0 for link in kill_links: db.expire(link.source, ['target_links']) kill_links.is_tombstone = True db.expire(idea, ['source_links']) db.flush() for parent in idea.get_parents(): new_ancestors.add(parent) new_ancestors.update(parent.get_all_ancestors()) for ancestor in new_ancestors ^ old_ancestors: ancestor.send_to_changes() else: order = idea_data.get('order', None) if order is not None: new_parent_id = Idea.get_database_id(new_parent_id) parent_links = [ link for link in idea.source_links if link.source_id == new_parent_id ] assert len(parent_links) == 1 parent_links[0].order = idea_data.get('order', 0.0) if 'subtype' in idea_data: idea.rdf_type = idea_data['subtype'] idea.send_to_changes() return {'ok': True, 'id': idea.uri()}
def save_idea(request): discussion_id = int(request.matchdict['discussion_id']) idea_id = request.matchdict['id'] idea_data = json.loads(request.body) #Idea.default_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'])) for parent_link in idea.source_links: # still assuming there's only one. pl_parent = parent_link.source pl_ancestors = pl_parent.get_all_ancestors() new_ancestors.update(pl_ancestors) if parent_link.source != parent: parent_link.copy(True) parent_link.source = parent parent.db.expire(parent, ['target_links']) parent.db.expire(pl_parent, ['target_links']) for ancestor in pl_ancestors: if ancestor in prev_ancestors: break ancestor.send_to_changes() for ancestor in prev_ancestors: if ancestor in new_ancestors: break ancestor.send_to_changes() parent_link.order = order parent_link.db.expire(parent_link.source, ['target_links']) parent_link.source.send_to_changes() parent_link.db.flush() idea.is_in_next_synthesis = idea_data.get('inNextSynthesis', False) idea.send_to_changes() return {'ok': True, 'id': idea.uri()}
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() }
def save_idea(request): """Update this idea. In case the ``parentId`` is changed, handle all ``IdeaLink`` changes and send relevant ideas on the socket.""" discussion_id = int(request.matchdict['discussion_id']) idea_id = request.matchdict['id'] idea_data = json.loads(request.body) #Idea.default_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)) simple_fields = { 'shortTitle': 'short_title', 'longTitle': 'long_title', 'definition': 'definition', 'message_view_override': 'message_view_override', 'messages_in_parent': 'messages_in_parent', } for key, attr_name in simple_fields.iteritems(): if key in idea_data: setattr(idea, attr_name, idea_data[key]) 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']) if not parent: raise HTTPNotFound("Missing parentId %s" % (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) for parent_link in idea.source_links: # still assuming there's only one. pl_parent = parent_link.source pl_ancestors = pl_parent.get_all_ancestors() new_ancestors.update(pl_ancestors) if parent_link.source != parent: parent_link.copy(True) parent_link.source = parent parent.db.expire(parent, ['target_links']) parent.db.expire(pl_parent, ['target_links']) for ancestor in pl_ancestors: if ancestor in prev_ancestors: break ancestor.send_to_changes() for ancestor in prev_ancestors: if ancestor in new_ancestors: break ancestor.send_to_changes() parent_link.order = order parent_link.db.expire(parent_link.source, ['target_links']) parent_link.source.send_to_changes() parent_link.db.flush() idea.send_to_changes() return {'ok': True, 'id': idea.uri()}
def save_idea(request): """Update this idea. In case the ``parentId`` is changed, handle all ``IdeaLink`` changes and send relevant ideas on the socket.""" discussion = request.context user_id = authenticated_userid(request) permissions = request.permissions idea_id = request.matchdict['id'] idea_data = json.loads(request.body) # Idea.default_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.") if (idea.discussion_id != discussion.id): raise HTTPBadRequest( "Idea from discussion %s cannot be saved from different discussion (%s)." % (idea.discussion_id, discussion.id)) context = idea.get_instance_context(request=request) for key, attr_name in langstring_fields.items(): if key in idea_data: current = getattr(idea, attr_name) ls_data = idea_data[key] # TODO: handle legacy string instance? subcontext = idea.get_collection_context(key, context) if current: if ls_data: current.update_from_json(ls_data, context=subcontext, permissions=permissions) else: current.delete() elif ls_data: current = LangString.create_from_json(ls_data, context=subcontext) setattr(idea, attr_name, current._instance) 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'])) for parent_link in idea.source_links: # still assuming there's only one. pl_parent = parent_link.source pl_ancestors = pl_parent.get_all_ancestors() new_ancestors.update(pl_ancestors) if parent_link.source != parent: parent_link.copy(True) parent_link.source = parent parent.db.expire(parent, ['target_links']) parent.db.expire(pl_parent, ['target_links']) for ancestor in pl_ancestors: if ancestor in prev_ancestors: break ancestor.send_to_changes() for ancestor in prev_ancestors: if ancestor in new_ancestors: break ancestor.send_to_changes() parent_link.order = order parent_link.db.expire(parent_link.source, ['target_links']) parent_link.source.send_to_changes() parent_link.db.flush() if 'subtype' in idea_data: idea.rdf_type = idea_data['subtype'] idea.send_to_changes() return {'ok': True, 'id': idea.uri()}