def vote_proposal(request, test_session, discussion, graphql_request, vote_session, graphql_registry): mutation = graphql_registry['createProposal'] vote_session_id = to_global_id("VoteSession", vote_session.id) from assembl.graphql.schema import Schema as schema res = schema.execute(mutation, context_value=graphql_request, variable_values={ "voteSessionId": vote_session_id, "titleEntries": [ {"value": u"Comprendre les dynamiques et les enjeux", "localeCode": "fr"}, {"value": u"Understanding the dynamics and issues", "localeCode": "en"} ], "descriptionEntries": [ {"value": u"Description: Comprendre les dynamiques et les enjeux", "localeCode": "fr"}, {"value": u"Description: Understanding the dynamics and issues", "localeCode": "en"} ] }) assert res.errors is None from assembl.graphql.utils import get_root_thematic_for_phase root_thematic = get_root_thematic_for_phase(vote_session.discussion_phase) proposal = root_thematic.children[0] def fin(): print "finalizer vote_proposal" test_session.delete(proposal) test_session.delete(root_thematic) test_session.flush() request.addfinalizer(fin) return proposal
def get_ideas(phase, options=None): root_thematic = get_root_thematic_for_phase(phase) discussion = phase.discussion if root_thematic is None: return [] model = models.Idea query = model.query if discussion.root_idea == root_thematic: # thread phase is directly on discussion.root_idea, # we need to remove all descendants of all the other phases descendants_to_ignore = get_descendants(get_all_phase_root_ideas(discussion)) descendants = root_thematic.get_all_descendants(id_only=True, inclusive=False) descendants = set(descendants) - set(descendants_to_ignore) query = query.filter(model.id.in_(descendants)) else: descendants_query = root_thematic.get_descendants_query(inclusive=False) query = query.filter(model.id.in_(descendants_query)) query = query.outerjoin( models.Idea.source_links ).filter( ~model.sqla_type.in_(('question', 'vote_proposal')), model.hidden == False, # noqa: E712 model.tombstone_date == None # noqa: E711 ).options( contains_eager(models.Idea.source_links), joinedload(models.Idea.title).joinedload("entries"), joinedload(models.Idea.description).joinedload("entries"), ).order_by(models.IdeaLink.order, models.Idea.creation_date) if options is not None: query = query.options(*options) return query
def get_ideas(phase, options=None): root_thematic = get_root_thematic_for_phase(phase) discussion = phase.discussion if root_thematic is None: return [] model = models.Idea query = model.query if discussion.root_idea == root_thematic: # thread phase is directly on discussion.root_idea, # we need to remove all descendants of all the other phases descendants_to_ignore = get_descendants( get_all_phase_root_ideas(discussion)) descendants = root_thematic.get_all_descendants(id_only=True, inclusive=False) descendants = set(descendants) - set(descendants_to_ignore) query = query.filter(model.id.in_(descendants)) else: descendants_query = root_thematic.get_descendants_query( inclusive=False) query = query.filter(model.id.in_(descendants_query)) query = query.outerjoin(models.Idea.source_links).filter( ~model.sqla_type.in_(('question', 'vote_proposal')), model.hidden == False, # noqa: E712 model.tombstone_date == None # noqa: E711 ).options( contains_eager(models.Idea.source_links), joinedload(models.Idea.title).joinedload("entries"), joinedload(models.Idea.description).joinedload("entries"), ).order_by(models.IdeaLink.order, models.Idea.creation_date) if options is not None: query = query.options(*options) return query
def resolve_root_idea(self, args, context, info): discussion_id = context.matchdict['discussion_id'] discussion = models.Discussion.get(discussion_id) identifier = args.get('identifier') if identifier is None or identifier == 'thread': return discussion.root_idea root_thematic = get_root_thematic_for_phase(discussion, identifier) return root_thematic
def resolve_thematics(self, args, context, info): identifier = args.get('identifier', None) discussion_id = context.matchdict['discussion_id'] discussion = models.Discussion.get(discussion_id) root_thematic = get_root_thematic_for_phase(discussion, identifier) if root_thematic is None: return [] return root_thematic.get_children()
def resolve_root_idea(self, args, context, info): discussion_id = context.matchdict['discussion_id'] discussion = models.Discussion.get(discussion_id) discussion_phase_id = args.get('discussion_phase_id') if not discussion_phase_id: return discussion.root_idea discussion_phase = models.DiscussionPhase.get(discussion_phase_id) root_thematic = get_root_thematic_for_phase(discussion_phase) return root_thematic
def resolve_root_idea(self, args, context, info): discussion_id = context.matchdict['discussion_id'] discussion = models.Discussion.get(discussion_id) discussion_phase_id = args.get('discussion_phase_id') if not discussion_phase_id: return discussion.root_idea discussion_phase = models.DiscussionPhase.get(discussion_phase_id) root_thematic = get_root_thematic_for_phase(discussion_phase) return root_thematic
def get_posts_for_phases(discussion, identifiers, include_deleted=False): """Return related posts for the given phases `identifiers` on `discussion`. """ # Retrieve the phases with posts identifiers_with_posts = [i for i in identifiers if i in PHASES_WITH_POSTS] if not discussion or not identifiers_with_posts: return None ideas = [] # If survey phase, we need the root thematic if Phases.survey.value in identifiers_with_posts: survey_phase = get_phase_by_identifier(discussion, Phases.survey.value) if survey_phase: root_thematic = get_root_thematic_for_phase(survey_phase) if root_thematic: ideas.append(root_thematic) identifiers_with_posts.remove(Phases.survey.value) if identifiers_with_posts: # If we have both 'thread' and 'multiColumns' in identifiers_with_posts # use get_ideas with 'thread' phase to get all ideas. # If only 'multiColumns' in identifiers_with_posts, use 'multiColumns' phase. # Ideas from 'multiColumns' phase are a subset of the ideas # from 'thread' phase is_multi_columns = Phases.multiColumns.value in identifiers_with_posts and \ len(identifiers_with_posts) == 1 if is_multi_columns: multi_columns_phase = get_phase_by_identifier(discussion, Phases.multiColumns.value) if multi_columns_phase: ideas.extend(get_ideas(multi_columns_phase).all()) else: thread_phase = get_phase_by_identifier(discussion, Phases.thread.value) if thread_phase: ideas.extend(get_ideas(thread_phase).all()) if not ideas: return None model = models.AssemblPost query = discussion.db.query(model) queries = [] for idea in ideas: related = idea.get_related_posts_query(True) related_query = query.join( related, model.id == related.c.post_id ) queries.append(related_query) query = queries[0].union_all(*queries[1:]) if not include_deleted: return query.filter( model.publication_state == models.PublicationStates.PUBLISHED) return query
def get_posts_for_phases(discussion, identifiers, include_deleted=False, include_moderating=None): """Return related posts for the ideas with module type in `identifiers` on `discussion`. """ if not discussion: return None module_types = identifiers[:] if Phases.multiColumns.value in module_types: # backward compatibility with bluenove-actionable module_types.remove(Phases.multiColumns.value) module_types.append(MessageView.messageColumns.value) ideas = [] for phase in discussion.timeline_events: root_thematic = get_root_thematic_for_phase(phase) if root_thematic is not None: ideas_query = get_ideas(phase) ideas.extend( ideas_query.filter( models.Idea.message_view_override.in_(module_types)).all()) if not ideas: return None model = models.AssemblPost query = discussion.db.query(model) queries = [] for idea in ideas: # Note we are not sending user_id related = idea.get_related_posts_query(True, include_deleted, include_moderating) related_query = query.join(related, model.id == related.c.post_id) queries.append(related_query) query = queries[0].union_all(*queries[1:]) if not include_deleted: return query.filter( model.publication_state == models.PublicationStates.PUBLISHED) return query
def get_ideas_for_export(discussion, module_type=None, start=None, end=None): ideas = [] for phase in discussion.timeline_events: # If [start, end] and [phase.start, phase.end] don't overlap, # don't export the ideas. if phase.end < start or phase.start > end: continue root_thematic = get_root_thematic_for_phase(phase) if root_thematic is not None: ideas_query = get_ideas(phase) if module_type is None: ideas.extend(ideas_query.all()) else: ideas.extend( ideas_query.filter(models.Idea.message_view_override == module_type).all()) return ideas
def get_posts_for_phases( discussion, identifiers, include_deleted=False, include_moderating=None): """Return related posts for the ideas with module type in `identifiers` on `discussion`. """ if not discussion: return None module_types = identifiers[:] if Phases.multiColumns.value in module_types: # backward compatibility with bluenove-actionable module_types.remove(Phases.multiColumns.value) module_types.append(MessageView.messageColumns.value) ideas = [] for phase in discussion.timeline_events: root_thematic = get_root_thematic_for_phase(phase) if root_thematic is not None: ideas_query = get_ideas(phase) ideas.extend(ideas_query.filter( models.Idea.message_view_override.in_(module_types)).all()) if not ideas: return None model = models.AssemblPost query = discussion.db.query(model) queries = [] for idea in ideas: # Note we are not sending user_id related = idea.get_related_posts_query( True, include_deleted, include_moderating) related_query = query.join( related, model.id == related.c.post_id ) queries.append(related_query) query = queries[0].union_all(*queries[1:]) if not include_deleted: return query.filter( model.publication_state == models.PublicationStates.PUBLISHED) return query
def get_ideas(phase, options=None): phase_identifier = phase.identifier root_thematic = get_root_thematic_for_phase(phase) discussion = phase.discussion if root_thematic is None: return [] model = models.Idea query = model.query if discussion.root_idea == root_thematic: # thread phase is directly on discussion.root_idea, # we need to remove all descendants of all the other phases descendants_to_ignore = get_descendants(get_all_phase_root_ideas(discussion)) descendants = root_thematic.get_all_descendants(id_only=True, inclusive=False) descendants = set(descendants) - set(descendants_to_ignore) query = query.filter(model.id.in_(descendants)) else: descendants_query = root_thematic.get_descendants_query(inclusive=False) query = query.filter(model.id.in_(descendants_query)) query = query.outerjoin( models.Idea.source_links ).filter( model.sqla_type != 'question', model.hidden == False, # noqa: E712 model.tombstone_date == None # noqa: E711 ).options( contains_eager(models.Idea.source_links), joinedload(models.Idea.title).joinedload("entries"), joinedload(models.Idea.description).joinedload("entries"), ).order_by(models.IdeaLink.order, models.Idea.creation_date) if options is not None: query = query.options(*options) if phase_identifier == Phases.multiColumns.value: # Filter out ideas that don't have columns. query = query.filter( models.Idea.message_view_override == 'messageColumns') return query
def get_ideas(phase, options=None): root_thematic = get_root_thematic_for_phase(phase) if root_thematic is None: return [] model = models.Idea query = model.query descendants_query = root_thematic.get_descendants_query(inclusive=False) query = query.filter(model.id.in_(descendants_query)) query = query.outerjoin(models.Idea.source_links).filter( ~model.sqla_type.in_(('question', 'vote_proposal')), model.hidden == False, # noqa: E712 model.tombstone_date == None # noqa: E711 ).options( contains_eager(models.Idea.source_links), joinedload(models.Idea.title).joinedload("entries"), joinedload(models.Idea.description).joinedload("entries"), ).order_by(models.IdeaLink.order, models.Idea.creation_date) if options is not None: query = query.options(*options) return query
def test_mutation_create_proposal_no_root_thematic(graphql_request, discussion, vote_session, graphql_registry): mutation = graphql_registry['createProposal'] vote_session_id = to_global_id("VoteSession", vote_session.id) root_thematic = get_root_thematic_for_phase(vote_session.discussion_phase) vote_session.discussion_phase.root_idea = None root_thematic.delete() root_thematic.db.flush() res = schema.execute(mutation, context_value=graphql_request, variable_values={ "voteSessionId": vote_session_id, "titleEntries": [ {"value": u"Comprendre les dynamiques et les enjeux", "localeCode": "fr"}, {"value": u"Understanding the dynamics and issues", "localeCode": "en"} ], "descriptionEntries": [ {"value": u"Description: Comprendre les dynamiques et les enjeux", "localeCode": "fr"}, {"value": u"Description: Understanding the dynamics and issues", "localeCode": "en"} ] }) assert len(res.errors) == 1 assert 'no root thematic' in res.errors[0].message
def test_mutation_create_proposal(graphql_request, discussion, vote_session, graphql_registry): mutation = graphql_registry['createProposal'] vote_session_id = to_global_id("VoteSession", vote_session.id) res = schema.execute(mutation, context_value=graphql_request, variable_values={ "voteSessionId": vote_session_id, "titleEntries": [ {"value": u"Comprendre les dynamiques et les enjeux", "localeCode": "fr"}, {"value": u"Understanding the dynamics and issues", "localeCode": "en"} ], "descriptionEntries": [ {"value": u"Description: Comprendre les dynamiques et les enjeux", "localeCode": "fr"}, {"value": u"Description: Understanding the dynamics and issues", "localeCode": "en"} ] }) assert res.errors is None root_thematic = get_root_thematic_for_phase(vote_session.discussion_phase) proposal = root_thematic.children[0] proposal_id = to_global_id("Idea", proposal.id) assert json.loads(json.dumps(res.data)) == { u'createProposal': { u'proposal': { u'id': proposal_id, u'order': 1.0, u'titleEntries': [ {u'localeCode': u'en', u'value': u'Understanding the dynamics and issues'}, {u'localeCode': u'fr', u'value': u'Comprendre les dynamiques et les enjeux'}], u'descriptionEntries': [ {u'localeCode': u'en', u'value': u'Description: Understanding the dynamics and issues'}, {u'localeCode': u'fr', u'value': u'Description: Comprendre les dynamiques et les enjeux'}] }}} # remove created proposal proposal.delete() proposal.db.flush()
def resolve_ideas(self, args, context, info): discussion_id = context.matchdict['discussion_id'] discussion = models.Discussion.get(discussion_id) phase_identifier = args.get('identifier') if phase_identifier in ('survey', 'brightMirror'): root_thematic = get_root_thematic_for_phase( discussion, phase_identifier) if root_thematic is None: return [] return root_thematic.get_children() model = models.Idea query = get_query(model, context) descendants_query = discussion.root_idea.get_descendants_query( inclusive=False) query = query.outerjoin(models.Idea.source_links).filter( model.id.in_(descendants_query) ).filter( model.hidden == False, # noqa: E712 model.sqla_type == 'idea', model.tombstone_date == None # noqa: E711 ).options( contains_eager(models.Idea.source_links), subqueryload(models.Idea.attachments).joinedload("document"), # subqueryload(models.Idea.message_columns), joinedload(models.Idea.title).joinedload("entries"), # joinedload(models.Idea.synthesis_title).joinedload("entries"), joinedload(models.Idea.description).joinedload("entries"), ).order_by(models.IdeaLink.order, models.Idea.creation_date) if phase_identifier == 'multiColumns': # Filter out ideas that don't have columns. query = query.filter( models.Idea.message_view_override == 'messageColumns') return query
def test_graphql_update_vote_session(graphql_request, vote_session, test_app, graphql_registry): mutate_and_assert(graphql_request, vote_session.discussion_phase_id, test_app, graphql_registry) root_thematic = get_root_thematic_for_phase(vote_session.discussion_phase) assert root_thematic is not None