def test_ongoing_poll_callback_agenda_item_not_ongoing_error(self): root = active_poll_fixture(self.config) ai = root['meeting']['ai'] security.unrestricted_wf_transition_to(ai, 'upcoming') self.assertRaises(HTTPForbidden, security.unrestricted_wf_transition_to, ai['poll'], 'ongoing')
def _setup_poll_fixture(config): config.testing_securitypolicy('admin', permissive = True) config.include('pyramid_chameleon') #Register plugin config.include('voteit.schulze') config.include('voteit.core.helpers') config.include('voteit.core.testing_helpers.register_catalog') root = bootstrap_and_fixture(config) root['m'] = Meeting() unrestricted_wf_transition_to(root['m'], 'ongoing') root['m']['ai'] = ai = AgendaItem() unrestricted_wf_transition_to(ai, 'upcoming') unrestricted_wf_transition_to(ai, 'ongoing') #Setup poll ai['poll'] = Poll() poll = ai['poll'] #Add proposals p1 = Proposal(creators = ['dummy'], text = 'first proposal') p1.uid = 'p1uid' #To make it simpler to test against ai['p1'] = p1 p2 = Proposal(creators = ['dummy'], text = 'second proposal') p2.uid = 'p2uid' ai['p2'] = p2 p3 = Proposal(creators = ['dummy'], text = 'third proposal') p3.uid = 'p3uid' ai['p3'] = p3 #Select proposals for this poll poll.proposal_uids = (p1.uid, p2.uid, p3.uid) #Set poll as ongoing unrestricted_wf_transition_to(poll, 'upcoming') unrestricted_wf_transition_to(poll, 'ongoing') return poll
def test_update_contained_in_ai(self): self.config.include('arche.testing.setup_auth') self.config.include('arche.testing') self.config.include('voteit.core.models.meeting') self.config.include('voteit.core.models.discussion_post') from voteit.core.models.discussion_post import DiscussionPost from voteit.core.models.meeting import Meeting from voteit.core.models.user import User root = self._fixture() root['users']['john'] = User() root['m'] = Meeting() root['m'].add_groups('john', [security.ROLE_VIEWER]) root['m']['ai'] = ai = self._ai(title='New object') ai['dp'] = dp = DiscussionPost() #To make sure dp got catalogued self.assertEqual(root.catalog.search(uid=dp.uid)[0], 1) #The discussion post shouldn't be visible now, since the ai is private self.assertEqual( root.catalog.search(uid=dp.uid, allowed_to_view=[security.ROLE_VIEWER])[0], 0) #When the ai is made upcoming, it should be visible security.unrestricted_wf_transition_to(ai, 'upcoming') self.assertEqual( root.catalog.search(uid=dp.uid, allowed_to_view=[security.ROLE_VIEWER])[0], 1)
def test_integration(self): self.config.scan('voteit.core.subscribers.unread') prop = self._fixture() unread = self.config.registry.getAdapter(prop, IUnread) self.assertEqual(unread.get_unread_userids(), frozenset([u'admin'])) unrestricted_wf_transition_to(prop, u'retracted') self.assertEqual(unread.get_unread_userids(), frozenset())
def test_ongoing_meeting_upcoming_ai(self): m = self._fixture() security.unrestricted_wf_transition_to(m, 'ongoing') security.unrestricted_wf_transition_to(m['ai'], 'upcoming') self.assertEqual(principals_allowed_by_permission(m['ai']['d'], security.VIEW), admin | viewer | moderator) self.assertEqual(principals_allowed_by_permission(m['ai']['d'], security.EDIT), admin | moderator) self.assertEqual(principals_allowed_by_permission(m['ai']['d'], security.DELETE), admin | moderator | owner)
def copy_ai(new_parent, ai, reset_wf=False, only_copy_prop_states=(), copy_types=()): """ :param new_parent: The new meeting object :param ai: object to be copied :param reset_wf: turn all proposals published? (bool) :param only_copy_prop_states: (list of states) :param copy_types: Copy these types, remove everything else. :return: """ # Note about copy: use zope.copy functions, check arche method 'copy_recursive' for that. new_ai = copy_recursive(ai) new_ai.state = unicode('private') counter = 1 #The current ai name = generate_slug(new_parent, new_ai.title) new_parent[name] = new_ai #Create new keys tuple to avoid changing lazy objects during iteration for key in tuple(new_ai.keys()): obj = new_ai[key] type_name = getattr(obj, 'type_name', '') if type_name not in copy_types: del new_ai[obj.__name__] continue if type_name == 'Proposal' and obj.get_workflow_state() not in only_copy_prop_states: del new_ai[obj.__name__] continue if reset_wf and obj.type_name == 'Proposal' and obj.get_workflow_state() != 'published': unrestricted_wf_transition_to(obj, 'published') # Kill anything else contained for k in obj.keys(): del obj[k] counter += 1 return counter
def test_only_published(self): lists = self._fixture() sl = lists['uid/1'] ai = sl.__parent__ for i in range(1, 3): unrestricted_wf_transition_to(ai['p_1_%s' % i], 'voting') res = self._fut(sl) self.assertEqual(res, 2) self.assertEqual(sl.speakers, [2, 3])
def test_upcoming_w_private_ai(self): poll = self._make_obj() request = testing.DummyRequest() security.unrestricted_wf_transition_to(poll, 'upcoming') self.assertEqual(self.pap(poll, security.VIEW), admin | moderator ) self.assertEqual(self.pap(poll, security.EDIT), admin | moderator) self.assertEqual(self.pap(poll, security.DELETE), admin | moderator) self.assertEqual(self.pap(poll, security.ADD_VOTE), set()) self.assertEqual(self.pap(poll, security.CHANGE_WORKFLOW_STATE), admin | moderator)
def test_email_voters_about_ongoing_poll(self): root = active_poll_fixture(self.config) poll = root['meeting']['ai']['poll'] request = testing.DummyRequest() mailer = get_mailer(request) self.assertEqual(len(mailer.outbox), 0) security.unrestricted_wf_transition_to(poll, 'ongoing') self.assertEqual(len(mailer.outbox), 1) self.failUnless('*****@*****.**' in mailer.outbox[0].recipients)
def test_closed_saves_voters(self): self.config.include('voteit.core.plugins.majority_poll') root = _voters_fixture(self.config) root['meeting'].del_groups('admin', [security.ROLE_VOTER]) poll = root['meeting']['ai']['poll'] poll.set_field_value('poll_plugin', 'majority_poll') security.unrestricted_wf_transition_to(poll, 'ongoing') security.unrestricted_wf_transition_to(poll, 'closed') self.assertEqual(poll.get_field_value('voters_mark_closed'), frozenset(['alice', 'ben', 'celine']))
def test_upcoming_w_private_ai(self): poll = self._make_obj() request = testing.DummyRequest() security.unrestricted_wf_transition_to(poll, 'upcoming') self.assertEqual(self.pap(poll, security.VIEW), admin | moderator) self.assertEqual(self.pap(poll, security.EDIT), admin | moderator) self.assertEqual(self.pap(poll, security.DELETE), admin | moderator) self.assertEqual(self.pap(poll, security.ADD_VOTE), set()) self.assertEqual(self.pap(poll, security.CHANGE_WORKFLOW_STATE), admin | moderator)
def test_ongoing_saves_voters(self): self.config.include('arche.testing') self.config.include('voteit.core.models.meeting') root = _voters_fixture(self.config) self.config.include('arche.testing.setup_auth') self.config.include('voteit.core.subscribers.poll') self.config.include('voteit.core.security') poll = root['meeting']['ai']['poll'] security.unrestricted_wf_transition_to(poll, 'ongoing') self.assertEqual(poll.get_field_value('voters_mark_ongoing'), frozenset(['alice', 'ben', 'celine', 'admin']))
def test_unrestricted_wf_transition_to(self): from voteit.core.models.meeting import Meeting root = self._fixture() request = testing.DummyRequest() obj = Meeting() #Regular wf method doesn't work self.assertRaises(WorkflowError, obj.set_workflow_state, request, 'ongoing') #But unrestricted does security.unrestricted_wf_transition_to(obj, 'ongoing') self.assertEqual(obj.get_workflow_state(), 'ongoing')
def test_change_states_of(self): self.config.scan('voteit.core.views.components') poll = self._fixture() ai = poll.__parent__ p1_uid = ai['prop1'].uid obj = self._cut(poll) self._add_votes(poll) unrestricted_wf_transition_to(poll, 'ongoing') unrestricted_wf_transition_to(poll, 'closed') result = obj.change_states_of() self.assertEqual(result, {p1_uid: 'approved'})
def test_handle_close(self): poll = self._fixture() obj = self._cut(poll) self._add_votes(poll) unrestricted_wf_transition_to(poll, 'ongoing') unrestricted_wf_transition_to(poll, 'closed') ai = poll.__parent__ p1_uid = ai['prop1'].uid p2_uid = ai['prop2'].uid expected = {p1_uid: {u'approve': 3, u'abstain': 1, u'deny': 0}, p2_uid: {u'approve': 1, u'abstain': 0, u'deny': 1}} self.assertEqual(poll.poll_result, expected)
def test_upcoming_meeting_upcoming_ai(self): m = self._fixture() security.unrestricted_wf_transition_to(m['ai'], 'upcoming') self.assertEqual( principals_allowed_by_permission(m['ai']['d'], security.VIEW), admin | viewer | moderator) self.assertEqual( principals_allowed_by_permission(m['ai']['d'], security.EDIT), admin | moderator) self.assertEqual( principals_allowed_by_permission(m['ai']['d'], security.DELETE), admin | moderator | owner)
def _fixture_and_setup(self): root = bootstrap_and_fixture(self.config) from voteit.core.models.user import User from voteit.core.models.agenda_item import AgendaItem #from voteit.core.models.meeting import Meeting from voteit.core.models.proposal import Proposal for userid in ('fredrik', 'anders', 'hanna', 'robin'): root.users[userid] = User() root['ai'] = AgendaItem() security.unrestricted_wf_transition_to(root['ai'], 'upcoming') root['ai']['p'] = Proposal() return root['ai']['p']
def _active_poll_fixture(config): config.testing_securitypolicy(userid='mrs_tester') config.include('voteit.core.plugins.majority_poll') config.include('voteit.core.testing_helpers.register_workflows') config.include('voteit.core.testing_helpers.register_catalog') root = active_poll_fixture(config) poll = root['meeting']['ai']['poll'] poll.set_field_value('poll_plugin', 'majority_poll') unrestricted_wf_transition_to(poll, 'ongoing') config.include('voteit.core.testing_helpers.register_security_policies') config.include('sfs_ga') return root['meeting']
def _setup_poll_fixture(config): # type: (Configurator) -> Poll config.testing_securitypolicy('admin', permissive=True) # config.include('pyramid_chameleon') # Register plugin config.include('voteit.stv') config.include('arche.testing') config.include('arche.testing.workflow') config.include('arche.testing.catalog') config.include('voteit.core.models.catalog') root = bootstrap_and_fixture(config) root['m'] = Meeting() unrestricted_wf_transition_to(root['m'], 'ongoing') root['m']['ai'] = ai = AgendaItem() unrestricted_wf_transition_to(ai, 'upcoming') unrestricted_wf_transition_to(ai, 'ongoing') #Setup poll ai['poll'] = Poll() poll = ai['poll'] #Add proposals p1 = Proposal(creators = ['dummy'], text = 'first proposal') p1.uid = 'p1uid' #To make it simpler to test against ai['p1'] = p1 p2 = Proposal(creators = ['dummy'], text = 'second proposal') p2.uid = 'p2uid' ai['p2'] = p2 p3 = Proposal(creators = ['dummy'], text = 'third proposal') p3.uid = 'p3uid' ai['p3'] = p3 #Select proposals for this poll poll.proposal_uids = (p1.uid, p2.uid, p3.uid) #Set poll as ongoing unrestricted_wf_transition_to(poll, 'upcoming') return poll
def test_change_states_no_vote(self): self.config.scan('voteit.core.views.components') poll = self._fixture() ai = poll.__parent__ p1_uid = ai['prop1'].uid p2_uid = ai['prop2'].uid obj = self._cut(poll) self._add_votes(poll) poll['v3'].set_vote_data({p2_uid: u'deny'}, notify = False) poll['v4'].set_vote_data({p2_uid: u'deny'}, notify = False) unrestricted_wf_transition_to(poll, 'ongoing') unrestricted_wf_transition_to(poll, 'closed') result = obj.change_states_of() self.assertEqual(result, {p1_uid: 'approved', p2_uid: 'denied'})
def test_render_result(self): poll = self._fixture() obj = self._cut(poll) self._add_votes(poll) unrestricted_wf_transition_to(poll, 'ongoing') unrestricted_wf_transition_to(poll, 'closed') ai = poll.__parent__ p1_uid = ai['prop1'].uid p2_uid = ai['prop2'].uid request = testing.DummyRequest() init_request_methods(request) request.root = find_root(poll) view = BaseView(poll, request) result = obj.render_result(view) self.assertIn('Proposal 1', result)
def test_render_result(self): self.config.scan('voteit.core.views.components') poll = self._fixture() obj = self._cut(poll) self._add_votes(poll) request = testing.DummyRequest() unrestricted_wf_transition_to(poll, 'ongoing') unrestricted_wf_transition_to(poll, 'closed') ai = poll.__parent__ p1_uid = ai['prop1'].uid p2_uid = ai['prop2'].uid request = testing.DummyRequest() api = APIView(poll, request) result = obj.render_result(request, api) self.assertIn('Proposal 1', result)
def test_unrestricted_wf_transition_to(self): self.config.include('pyramid_zcml') self.config.load_zcml('voteit.core:configure.zcml') #Load workflows from voteit.core.models.meeting import Meeting request = testing.DummyRequest() obj = Meeting() #Regular wf method doesn't work self.assertRaises(WorkflowError, obj.set_workflow_state, request, 'ongoing') #But unrestricted does security.unrestricted_wf_transition_to(obj, 'ongoing') self.assertEqual(obj.get_workflow_state(), 'ongoing')
def __call__(self): """ Start the decision process. * Block proposals * Make all proposals that aren't marked with 'bifall yrkat' unhandled. """ # context is the agenda item - block proposals self.context.proposal_block = True # Change all proposals that are published to unhandled, if they aren't marked as 'bifall yrkat' count = 0 for obj in self.context.values(): if IProposal.providedBy(obj) and obj.get_workflow_state( ) == 'published' and not getattr(obj, ATTR_NAME, False): unrestricted_wf_transition_to(obj, 'unhandled') count += 1 self.flash_messages.add("{} förslag justerade".format(count)) return HTTPFound(location=self.request.resource_url(self.context))
def _fixture(self): from arche.resources import Document from voteit.core.models.agenda_item import AgendaItem from voteit.core.models.proposal import Proposal from voteit.core.models.meeting import Meeting root = bootstrap_and_fixture(self.config) root['m'] = m = Meeting() m['ai'] = ai = AgendaItem(title='ai') ai['p'] = Proposal(text='Some animlas are more equal than others') ai['p']['d'] = Document() ai['p2'] = Proposal(text='Me wont be there') unrestricted_wf_transition_to(ai['p2'], 'retracted') root['m2'] = Meeting() request = testing.DummyRequest() request.root = root apply_request_extensions(request) self.config.begin(request) return root, request
def test_update_contained_in_ai(self): self.config.include('voteit.core.testing_helpers.register_security_policies') from voteit.core.models.discussion_post import DiscussionPost from voteit.core.models.meeting import Meeting from voteit.core.models.user import User root = self._fixture() root['users']['john'] = User() root['m'] = Meeting() root['m'].add_groups('john', [security.ROLE_VIEWER]) root['m']['ai'] = ai = self._ai(title = 'New object') ai['dp'] = dp = DiscussionPost() #To make sure dp got catalogued self.assertEqual(root.catalog.search(uid = dp.uid)[0], 1) #The discussion post shouldn't be visible now, since the ai is private self.assertEqual(root.catalog.search(uid = dp.uid, allowed_to_view = [security.ROLE_VIEWER])[0], 0) #When the ai is made upcoming, it should be visible security.unrestricted_wf_transition_to(ai, 'upcoming') self.assertEqual(root.catalog.search(uid = dp.uid, allowed_to_view = [security.ROLE_VIEWER])[0], 1)
def _setup_poll_fixture(config): #Register plugin config.include('voteit.schulze') config.include('voteit.core.testing_helpers.register_catalog') config.include('voteit.core.testing_helpers.register_security_policies') config.scan('voteit.core.subscribers.catalog') config.scan('voteit.core.views.components.proposals') config.scan('voteit.core.views.components.creators_info') config.scan('voteit.core.views.components.metadata_listing') root = bootstrap_and_fixture(config) root['m'] = Meeting() unrestricted_wf_transition_to(root['m'], 'ongoing') root['m']['ai'] = ai = AgendaItem() unrestricted_wf_transition_to(ai, 'upcoming') unrestricted_wf_transition_to(ai, 'ongoing') #Setup poll ai['poll'] = Poll() poll = ai['poll'] #Add proposals p1 = Proposal(creators = ['dummy']) p1.uid = 'p1uid' #To make it simpler to test against ai['p1'] = p1 p2 = Proposal(creators = ['dummy']) p2.uid = 'p2uid' ai['p2'] = p2 p3 = Proposal(creators = ['dummy']) p3.uid = 'p3uid' ai['p3'] = p3 #Select proposals for this poll poll.proposal_uids = (p1.uid, p2.uid, p3.uid) #Set poll as ongoing unrestricted_wf_transition_to(poll, 'upcoming') unrestricted_wf_transition_to(poll, 'ongoing') return poll
def test_delegators_get_votes(self): self.config.include('pyramid_chameleon') vote = _voting_fixture(self.config) self.config.testing_securitypolicy(userid = 'jane') poll = vote.__parent__ unrestricted_wf_transition_to(poll, 'ongoing') self.config.registry.settings['voteit.liquid.type'] = 'simple' self.config.include('voteit.liquid.models') obj = self._cut(vote) root = obj.meeting.__parent__ root.users['james'] = User() root.users['john'] = User() obj.meeting.add_groups('james', ['role:Voter']) obj.meeting.add_groups('john', ['role:Voter']) repr = IRepresentatives(obj.meeting) repr['jane'] = ('james', 'john') new_v = Vote(creators = ['jane']) new_v.set_vote_data({'a': 1}, notify = False) poll['jane'] = new_v self.assertIn('james', poll) self.assertIn('john', poll) self.assertEqual(poll['james'].get_vote_data(), {'a': 1})
def test_ongoing_wo_proposal(self): register_workflows(self.config) self.config.include('arche.testing.catalog') ai = self._agenda_item_with_proposals_fixture() ai['poll'] = poll = self._cut() #ai = find_interface(poll, IAgendaItem) security.unrestricted_wf_transition_to(ai, 'upcoming') security.unrestricted_wf_transition_to(ai, 'ongoing') # remove all proposals on poll #poll.set_field_value('proposals', set()) security.unrestricted_wf_transition_to(poll, 'upcoming') request = testing.DummyRequest() self.assertRaises(HTTPForbidden, poll.set_workflow_state, request, 'ongoing')
def _fixture(config): from voteit.core.models.agenda_item import AgendaItem from voteit.core.models.meeting import Meeting from voteit.core.models.proposal import Proposal from voteit.core.models.user import User from voteit.core.security import unrestricted_wf_transition_to request = testing.DummyRequest() root = bootstrap_and_fixture(config) root.users['dummy'] = User() root['m'] = meeting = Meeting() meeting.add_groups('dummy', ['role:Voter', 'role:Viewer']) meeting['ai1'] = AgendaItem() meeting['ai2'] = ai2 = AgendaItem() unrestricted_wf_transition_to(ai2, 'upcoming') ai2['p1'] = Proposal() ai2['p2'] = Proposal() meeting['ai3'] = ai3 = AgendaItem() unrestricted_wf_transition_to(ai3, 'upcoming') meeting['ai4'] = ai4 = AgendaItem() unrestricted_wf_transition_to(ai4, 'upcoming') return meeting
def _fixture(config): from voteit.core.models.agenda_item import AgendaItem from voteit.core.models.meeting import Meeting from voteit.core.models.proposal import Proposal from voteit.core.models.user import User from voteit.core.security import unrestricted_wf_transition_to request = testing.DummyRequest() root = bootstrap_and_fixture(config) root.users["dummy"] = User() root["m"] = meeting = Meeting() meeting.add_groups("dummy", ["role:Voter", "role:Viewer"]) meeting["ai1"] = AgendaItem() meeting["ai2"] = ai2 = AgendaItem() unrestricted_wf_transition_to(ai2, "upcoming") ai2["p1"] = Proposal() ai2["p2"] = Proposal() meeting["ai3"] = ai3 = AgendaItem() unrestricted_wf_transition_to(ai3, "upcoming") meeting["ai4"] = ai4 = AgendaItem() unrestricted_wf_transition_to(ai4, "upcoming") return meeting
def test_ongoing_saves_voters(self): root = _voters_fixture(self.config) poll = root['meeting']['ai']['poll'] security.unrestricted_wf_transition_to(poll, 'ongoing') self.assertEqual(poll.get_field_value('voters_mark_ongoing'), frozenset(['alice', 'ben', 'celine', 'admin']))
def poll_is_deleted(obj, event): """ Set proposals as published when a poll is deleted. Only do this if they're locked for vote.""" request = get_current_request() for proposal in obj.get_proposal_objects(): if proposal.get_workflow_state() == 'voting': unrestricted_wf_transition_to(proposal, 'published')