def test_get_author_name(self): feedback = Feedback(author=None) self.assertEqual(feedback.get_author_name(), "An Anonymous Contributor") user = UserFactory() feedback = FeedbackFactory(author=user) self.assertEqual(feedback.get_author_name(), user.username)
def test_model_feedbackcount_changes(self): decision = Decision(description="Decision test data") decision.save(self.user) self.instance_attribute_has_value(decision,"feedbackcount",0) feedback = Feedback(description="Feedback test data", decision=decision) feedback.save() self.instance_attribute_has_value(decision,"feedbackcount",1)
def create_and_return_example_decision_with_feedback(self): decision = self.create_and_return_decision() feedback = Feedback(description='No time to decide', decision=decision) feedback.save() return decision
def test_model_feedbackcount_changes(self): decision = Decision(description="Decision test data") decision.save(self.user) self.instance_attribute_has_value(decision, "feedbackcount", 0) feedback = Feedback(description="Feedback test data", decision=decision) feedback.save() self.instance_attribute_has_value(decision, "feedbackcount", 1)
def create_and_return_example_concensus_decision_with_feedback(self): decision = self.create_and_return_decision(status=Decision.DECISION_STATUS) feedback = Feedback(description='No time to decide', decision=decision) feedback.save() return decision
def test_model_feedbackcount_changes(self): decision = self.make_decision() self.instance_attribute_has_value(decision, "feedbackcount", 0) feedback = Feedback(description="Feedback test data", decision=decision, author=self.user) feedback.save() self.instance_attribute_has_value(decision, "feedbackcount", 1)
def create_and_return_example_concensus_decision_with_feedback(self): decision = self.create_and_return_decision( status=Decision.DECISION_STATUS) feedback = Feedback(description='No time to decide', decision=decision) feedback.save() return decision
def test_view_feedback(self): decision = self.create_and_return_decision() feedback = Feedback(description='test feedback', decision=decision) feedback.save() response = self.client.get(reverse('publicweb_feedback_detail', args=[feedback.id])) self.assertContains(response, u"Feedback") self.assertContains(response, feedback.description)
def test_feedback_linebreaks(self): decision = self.make_decision(description="Lorem") feedback = Feedback(description="text\ntext") feedback.decision = decision feedback.author = self.user feedback.save() path = reverse("publicweb_feedback_detail", args=[feedback.id]) response = self.client.get(path) self.assertContains(response, "text<br />text", 1, msg_prefix="Failed to line break text")
def test_view_feedback(self): decision = self.create_and_return_decision() feedback = Feedback(description='test feedback', decision=decision, author=self.user) feedback.save() response = self.client.get( reverse('publicweb_feedback_detail', args=[feedback.id])) self.assertContains(response, u"Feedback") self.assertContains(response, feedback.description)
def create_and_return_feedback(self,decision=None, description='Feedback', author=None): if author==None: author=self.user if decision==None: decision=self.create_and_return_decision() feedback = Feedback(description=description, decision=decision, author=author) feedback.save() return feedback
def test_feedback_author_shown(self): decision = self.make_decision(description="Lorem Ipsum") feedback = Feedback(description="Dolor sit") feedback.author = self.user feedback.decision = decision feedback.save() self.user = self.login('charlie') path = reverse('publicweb_item_detail', args=[decision.id]) response = self.client.get(path) betty = User.objects.get(username='******') self.assertContains(response, betty.first_name)
def create_and_return_feedback(self, decision=None, description='Feedback', author=None): if author == None: author = self.user if decision == None: decision = self.create_and_return_decision() feedback = Feedback(description=description, decision=decision, author=author) feedback.save() return feedback
def test_feedback_author_shown(self): self.user = self.login('Barry') decision = Decision(description="Lorem Ipsum") decision.save() feedback = Feedback(description="Dolor sit") feedback.author = self.user feedback.decision = decision feedback.save() self.user = self.login('Adam') path = reverse('publicweb_item_detail', args=[decision.id]) response = self.client.get(path) barry = User.objects.get(username='******') self.assertContains(response, barry.username)
def test_feedback_linebreaks(self): decision = self.make_decision(description="Lorem") feedback = Feedback(description="text\ntext") feedback.decision = decision feedback.author = self.user feedback.save() path = reverse('publicweb_feedback_detail', args=[feedback.id]) response = self.client.get(path) self.assertContains(response, 'text<br />text', 1, msg_prefix="Failed to line break text")
def _process_email(self, mail, verbosity): # pylint: disable=R0914 logger = logging.getLogger('econsensus') #handle multipart mails, cycle through mail #until find text type with a full payload. if mail.is_multipart(): for message in mail.get_payload(): if message.get_content_maintype() == 'text': msg_string = self._strip_string(message.get_payload(), verbosity) if msg_string: break else: msg_string = self._strip_string(mail.get_payload(), verbosity) if not msg_string: logger.error("[EMAIL REJECTED] From '%s' Reason: Email payload empty" % mail['From']) return #Must match email 'from' address to user from_match = re.search('([\w\-\.]+@\w[\w\-]+\.+[\w\-]+)', mail['From']) if from_match: self._print_if_verbose(verbosity, "Found email 'from' '%s'" % from_match.group(1)) try: user = User.objects.get(email=from_match.group(1)) self._print_if_verbose(verbosity, "Matched email to user '%s'" % user) except: logger.error("[EMAIL REJECTED] From '%s' Reason: Email address does not correspond to any known User" % mail['From']) return else: logger.error("[EMAIL REJECTED] From '%s' Reason: Unrecognised email address format" % mail['From']) return #Must match email 'to' address to organization org_match = re.search('([\w\-\.]+)@\w[\w\-]+\.+[\w\-]+', mail['To']) if org_match: self._print_if_verbose(verbosity, "Found email 'to' '%s'" % org_match.group(1)) try: organization = Organization.objects.get(slug=org_match.group(1)) self._print_if_verbose(verbosity, "Matched email to organization '%s'" % organization.name) except: logger.error("[EMAIL REJECTED] From '%s' Reason: '%s' does not correspond to any known Organization" \ % (mail['From'], org_match.group(1))) return else: logger.error("[EMAIL REJECTED] From '%s' Reason: Couldn't pull Organization from '%s'" % (mail['From'], mail['To'])) return #User must be a member of the Organization if organization not in Organization.active.get_for_user(user): self._print_if_verbose(verbosity, "User %s is not a member of Organization %s" % (user.username, organization.name)) logger.error("[EMAIL REJECTED] From '%s' Reason: User '%s' is not a member of Organization '%s'" \ % (mail['From'], user.username, organization.name)) return #Look for feedback types in the message body rating = Feedback.COMMENT_STATUS description = msg_string parse_feedback = re.match('(\w+)\s*:\s*([\s\S]*)', msg_string, re.IGNORECASE) if parse_feedback: description = parse_feedback.group(2) rating_match = re.match('question|danger|concerns|consent|comment', parse_feedback.group(1), re.IGNORECASE) if rating_match: self._print_if_verbose(verbosity, "Found feedback rating '%s'" % rating_match.group()) rating = rating_int(rating_match.group().lower()) # Determine whether email is in reply to a notification subject_match = re.search('\[(\d+)(?:\\\\(\d+)(?:\\\\(\d+))?)?\]', mail['Subject']) if subject_match: #process comment or feedback against feedback if subject_match.group(2): self._print_if_verbose(verbosity, "Found feedback id '%s' in Subject" % subject_match.group(2)) try: feedback = Feedback.objects.get(pk=subject_match.group(2)) except: logger.error("[EMAIL REJECTED] From '%s' Reason: id '%s' does not correspond to any known Feedback" \ % (mail['From'], subject_match.group(2))) return if parse_feedback and rating_match: decision = feedback.decision self._print_if_verbose(verbosity, "Creating feedback with rating '%s' and description '%s'." % (rating, description)) feedback = Feedback(author=user, decision=decision, rating=rating, description=description) feedback.save() logger.info("User '%s' added feedback via email to decision #%s" % (user, decision.id)) self._print_if_verbose(verbosity, "Found corresponding object '%s'" % decision.excerpt) else: comment_text = msg_string self._print_if_verbose(verbosity, "Creating comment '%s'." % (comment_text)) comment = Comment(user=user, comment = comment_text, content_object=feedback, object_pk=feedback.id, content_type=ContentType.objects.get(app_label="publicweb", model="feedback"), submit_date = timezone.now(), site = Site.objects.get_current()) comment.save() logger.info("User '%s' added comment via email to feedback #%s" % (user, feedback.id)) self._print_if_verbose(verbosity, "Found corresponding object '%s'" % feedback.description) #process feedback against decision elif subject_match.group(1): self._print_if_verbose(verbosity, "Found decision id '%s' in Subject" % subject_match.group(1)) try: decision = Decision.objects.get(pk=subject_match.group(1)) except: logger.error("[EMAIL REJECTED] From '%s' Reason: id '%s' does not correspond to any known Decision" \ % (mail['From'], subject_match.group(1))) return self._print_if_verbose(verbosity, "Creating feedback with rating '%s' and description '%s'." % (rating, description)) feedback = Feedback(author=user, decision=decision, rating=rating, description=description) feedback.save() logger.info("User '%s' added feedback via email to decision #%s" % (user, decision.id)) self._print_if_verbose(verbosity, "Found corresponding object '%s'" % decision.excerpt) else: self._print_if_verbose(verbosity, "No id found in message subject: %s" % mail['Subject']) logger.error("[EMAIL REJECTED] From '%s' Reason: No id present." \ % mail['From']) # Email was not in reply to a notification so create a new proposal else: proposal_match = re.search('proposal', mail['Subject'], re.IGNORECASE) if proposal_match: decision = Decision(author=user, editor=user, status=Decision.PROPOSAL_STATUS, organization=organization, \ description=msg_string) decision.save() self._print_if_verbose(verbosity, "User '%s' created decision #%s via email" % (user, decision.id)) logger.info("User '%s' created decision #%s via email" % (user, decision.id)) else: logger.error("[EMAIL REJECTED] From '%s' Reason: Email was not in reply to a notification and body didn't contain keyword 'proposal'" \ % mail['From'])
def test_list_page_sorted_by_feedback(self): # Create test decisions in reverse date order. decision1 = Decision(description="Apple", status=Decision.DECISION_STATUS) decision1.save(self.user) feedback = Feedback(description="One", decision=decision1) feedback.save() feedback = Feedback(description="Two", decision=decision1) feedback.save() feedback = Feedback(description="Three", decision=decision1) feedback.save() decision2 = Decision(description="Coconut", status=Decision.DECISION_STATUS) decision2.save() decision3 = Decision(description="Blackberry", status=Decision.DECISION_STATUS) decision3.save(self.user) feedback = Feedback(description="One", decision=decision3) feedback.save() feedback = Feedback(description="Two", decision=decision3) feedback.save() response = self.client.get(reverse('publicweb_item_list', args=['decision']), dict(sort='feedback')) object_list = response.context['object_list'] self.assertEquals(decision1.id, getattr(object_list[0], 'id')) self.assertEquals(decision3.id, getattr(object_list[1], 'id')) self.assertEquals(decision2.id, getattr(object_list[2], 'id'))
def test_feedback_can_have_empty_description(self): decision = Decision(description='Test', status=Decision.DECISION_STATUS) decision.save(self.user) feedback = Feedback(rating=Feedback.CONSENT_STATUS, decision=decision) self.instance_validates(feedback)
def test_list_pages_can_be_sorted(self): """Test that sort works for all Decision List columns we offer it on""" number_of_test_decisions = 3 # Two additional empty decisions will be made # these are the dates we'll set in the test random_dates = [ self._get_random_date() for i in range(number_of_test_decisions) ] random_descriptions = [ self._get_random_string(30) for i in range(number_of_test_decisions) ] # set all the columns you want to test sorting on date_columns = [ 'deadline', 'decided_date', 'review_date', 'archived_date' ] ############################# # Make the decisions ############################# # Make an initial empty decision decisions = [] decisions.append( self.make_decision(description="Decision None 1", organization=self.bettysorg)) # Create decisions with random data for i in range(number_of_test_decisions): d = self.make_decision(description=random_descriptions[i], organization=self.bettysorg) for column in date_columns: setattr(d, column, random_dates[i]) d.save() decisions.append(d) # Set random feedback (offset by 1 so empty decision remains empty) for i in range(1, number_of_test_decisions + 1): for f in range(randint(1, 4)): Feedback(description=self._get_random_string(10), decision=decisions[i], author=self.user).save() # Make a final empty decision decisions.append( self.make_decision(description="Decision None 2", organization=self.bettysorg)) # Get the last_modified & id values last_modifieds = [decision.last_modified for decision in decisions] ids = [decision.id for decision in decisions] feedbackcounts = [decision.feedbackcount() for decision in decisions] excerpts = [decision.excerpt for decision in decisions] ############################# # we need sorted values to compare against sorted_dates = sorted(random_dates) sorted_last_modifieds = sorted(last_modifieds) sorted_ids = sorted(ids) sorted_feedbackcounts = sorted(feedbackcounts) sorted_excerpts = sorted(excerpts, key=unicode.lower) # Test Dates for column in date_columns: response = self.client.get( reverse('publicweb_item_list', args=[self.bettysorg.slug, 'proposal']), dict(sort=column)) object_list = response.context['object_list'] for index, sorted_date in enumerate(sorted_dates): self.assertEquals(sorted_date, getattr(object_list[index], column), 'Testing date sort failed for' + column) self.assertEquals( None, getattr(object_list[len(object_list) - 2], column), 'Testing date sort failed for' + column) self.assertEquals( None, getattr(object_list[len(object_list) - 1], column), 'Testing date sort failed for' + column) # Test Last Modified response = self.client.get( reverse('publicweb_item_list', args=[self.bettysorg.slug, 'proposal']), {'sort': 'last_modified'}) object_list = response.context['object_list'] for index, sorted_last_modified in enumerate(sorted_last_modifieds): # Replace Microsecond to enable good results on MySQL and SQLLite sorted_list_last_modified = sorted_last_modified.replace( microsecond=0) object_list_last_modified = getattr( object_list[index], 'last_modified').replace(microsecond=0) self.assertEquals(sorted_list_last_modified, object_list_last_modified, 'Testing date sort failed for last_modified') # At this point, the ids in browser are all out of order, so good time to test id sort response = self.client.get( reverse('publicweb_item_list', args=[self.bettysorg.slug, 'proposal']), {'sort': 'id'}) object_list = response.context['object_list'] for index, sorted_id in enumerate(sorted_ids): self.assertEquals(sorted_id, getattr(object_list[index], 'id'), 'Testing id sort failed') # Test Feedback response = self.client.get( reverse('publicweb_item_list', args=[self.bettysorg.slug, 'proposal']), {'sort': 'feedback'}) object_list = response.context['object_list'] for index, sorted_feedbackcount in enumerate(sorted_feedbackcounts): self.assertEquals(sorted_feedbackcount, object_list[index].feedbackcount(), 'Testing feedbackcount sort failed.') # Test Excerpt response = self.client.get( reverse('publicweb_item_list', args=[self.bettysorg.slug, 'proposal']), {'sort': 'excerpt'}) object_list = response.context['object_list'] for index, sorted_excerpt in enumerate(sorted_excerpts): self.assertEquals(sorted_excerpt, getattr(object_list[index], 'excerpt'), 'Testing excerpt sort failed')
def _process_email(self, mail, verbosity): # pylint: disable=R0914 logger = logging.getLogger('econsensus') #handle multipart mails, cycle through mail #until find text type with a full payload. if mail.is_multipart(): for message in mail.get_payload(): if message.get_content_maintype() == 'text': msg_string = self._strip_string(message.get_payload(), verbosity) if msg_string: break else: msg_string = self._strip_string(mail.get_payload(), verbosity) if not msg_string: logger.error( "[EMAIL REJECTED] From '%s' Reason: Email payload empty" % mail['From']) return #Must match email 'from' address to user from_match = re.search('([\w\-\.]+@\w[\w\-]+\.+[\w\-]+)', mail['From']) if from_match: self._print_if_verbose( verbosity, "Found email 'from' '%s'" % from_match.group(1)) try: user = User.objects.get(email=from_match.group(1)) except ObjectDoesNotExist: logger.error("[EMAIL REJECTED] From '%s' Reason: id '%s' does not correspond to any known User" \ % (mail['From'], from_match.group(1))) return except MultipleObjectsReturned: logger.error("[EMAIL REJECTED] From '%s' Reason: Query returned several Users for id '%s'" \ % (mail['From'], from_match.group(1))) return self._print_if_verbose(verbosity, "Matched email to user '%s'" % user) else: logger.error( "[EMAIL REJECTED] From '%s' Reason: Unrecognised email address format" % mail['From']) return #Must match email 'to' address to organization org_match = re.search('([\w\-\.]+)@\w[\w\-]+\.+[\w\-]+', mail['To']) if org_match: self._print_if_verbose( verbosity, "Found email 'to' '%s'" % org_match.group(1)) try: organization = Organization.objects.get( slug=org_match.group(1)) except ObjectDoesNotExist: logger.error("[EMAIL REJECTED] From '%s' Reason: id '%s' does not correspond to any known Organization" \ % (mail['From'], org_match.group(1))) return except MultipleObjectsReturned: logger.error("[EMAIL REJECTED] From '%s' Reason: Query returned several Organizations for id '%s'" \ % (mail['From'], org_match.group(1))) return self._print_if_verbose( verbosity, "Matched email to organization '%s'" % organization.name) else: logger.error( "[EMAIL REJECTED] From '%s' Reason: Couldn't pull Organization from '%s'" % (mail['From'], mail['To'])) return #User must be a member of the Organization if organization not in Organization.active.get_for_user(user): self._print_if_verbose( verbosity, "User %s is not a member of Organization %s" % (user.username, organization.name)) logger.error("[EMAIL REJECTED] From '%s' Reason: User '%s' is not a member of Organization '%s'" \ % (mail['From'], user.username, organization.name)) return #Look for feedback types in the message body rating = Feedback.COMMENT_STATUS description = msg_string parse_feedback = re.match('(\w+)\s*:\s*([\s\S]*)', msg_string, re.IGNORECASE) if parse_feedback: description = parse_feedback.group(2) rating_match = re.match('question|danger|concerns|consent|comment', parse_feedback.group(1), re.IGNORECASE) if rating_match: self._print_if_verbose( verbosity, "Found feedback rating '%s'" % rating_match.group()) rating = dict(Feedback.RATING_CHOICES).values().index( rating_match.group().lower()) # Determine whether email is in reply to a notification subject_match = re.search('\[EC#(\d+)(?:\\\\(\d+)(?:\\\\(\d+))?)?\]', mail['Subject']) if subject_match: #Check that the user has the right to comment against the decision. if subject_match.group(1): self._print_if_verbose( verbosity, "Found decision id '%s' in Subject" % subject_match.group(1)) try: decision = Decision.objects.get(pk=subject_match.group(1)) except ObjectDoesNotExist: logger.error("[EMAIL REJECTED] From '%s' Reason: id '%s' does not correspond to any known Decision" \ % (mail['From'], subject_match.group(1))) return except MultipleObjectsReturned: logger.error("[EMAIL REJECTED] From '%s' Reason: Query returned several Decisions for id '%s'" \ % (mail['From'], subject_match.group(1))) return if user not in decision.organization.users.all(): logger.error("[EMAIL REJECTED] From '%s' Reason: User cannot reply to decision #%s because they are not a member of that organization." \ % (mail['From'], subject_match.group(1))) return #process comment or feedback against feedback if subject_match.group(2): self._print_if_verbose( verbosity, "Found feedback id '%s' in Subject" % subject_match.group(2)) try: feedback = Feedback.objects.get(pk=subject_match.group(2)) except ObjectDoesNotExist: logger.error("[EMAIL REJECTED] From '%s' Reason: id '%s' does not correspond to any known Feedback" \ % (mail['From'], subject_match.group(2))) return except MultipleObjectsReturned: logger.error("[EMAIL REJECTED] From '%s' Reason: Query returned more than one Feedback for id '%s'" \ % (mail['From'], subject_match.group(2))) return if parse_feedback and rating_match: decision = feedback.decision self._print_if_verbose( verbosity, "Creating feedback with rating '%s' and description '%s'." % (rating, description)) feedback = Feedback(author=user, decision=decision, rating=rating, description=description) feedback.save() logger.info( "User '%s' added feedback via email to decision #%s" % (user, decision.id)) self._print_if_verbose( verbosity, "Found corresponding object '%s'" % decision.excerpt) else: comment_text = msg_string self._print_if_verbose( verbosity, "Creating comment '%s'." % (comment_text)) comment = Comment(user=user, user_name=user.get_full_name(), user_email=user.email, comment=comment_text, content_object=feedback, object_pk=feedback.id, content_type=ContentType.objects.get( app_label="publicweb", model="feedback"), submit_date=timezone.now(), site=Site.objects.get_current()) comment.save() logger.info( "User '%s' added comment via email to feedback #%s" % (user, feedback.id)) self._print_if_verbose( verbosity, "Found corresponding object '%s'" % feedback.description) #process feedback against decision elif subject_match.group(1): self._print_if_verbose( verbosity, "Creating feedback with rating '%s' and description '%s'." % (rating, description)) feedback = Feedback(author=user, decision=decision, rating=rating, description=description) feedback.save() logger.info( "User '%s' added feedback via email to decision #%s" % (user, decision.id)) self._print_if_verbose( verbosity, "Found corresponding object '%s'" % decision.excerpt) else: self._print_if_verbose( verbosity, "No id found in message subject: %s" % mail['Subject']) logger.error("[EMAIL REJECTED] From '%s' Reason: No id present." \ % mail['From']) # Email was not in reply to a notification so create a new proposal else: proposal_match = re.search('proposal', mail['Subject'], re.IGNORECASE) if proposal_match: decision = Decision(author=user, editor=user, status=Decision.PROPOSAL_STATUS, organization=organization, \ description=msg_string) decision.save() self._print_if_verbose( verbosity, "User '%s' created decision #%s via email" % (user, decision.id)) logger.info("User '%s' created decision #%s via email" % (user, decision.id)) else: logger.error("[EMAIL REJECTED] From '%s' Reason: Email was not in reply to a notification and body didn't contain keyword 'proposal'" \ % mail['From'])
def test_feedback_can_have_empty_description(self): decision = self.make_decision() feedback = Feedback(rating=Feedback.CONSENT_STATUS, decision=decision) self.instance_validates(feedback)
def test_feedback_has_author(self): decision = self.make_decision() feedback = Feedback(description="Feedback test data", decision=decision) self.model_has_attribute(feedback, "author")
def _process_email(self, mail, verbosity): # pylint: disable=R0914 user = None decision = None user_found = False object_found = False org_found = False msg_string = '' #match email 'from' address to user from_match = re.search('([\w\-\.]+@\w[\w\-]+\.+[\w\-]+)', mail['From']) if from_match: self._print_if_verbose(verbosity, "Found email 'from' '%s'" % from_match.group(1)) try: user = User.objects.get(email=from_match.group(1)) user_found = True self._print_if_verbose(verbosity, "Matched email to user '%s'" % user) except: pass #match id to object id_match = re.search('#(\d+)', mail['Subject']) if id_match: self._print_if_verbose(verbosity, "Found '%s' in Subject" % id_match.group()) try: decision = Decision.objects.get(pk=id_match.group(1)) object_found = True self._print_if_verbose(verbosity, "Found corresponding object '%s'" % decision.excerpt) except: pass #match email 'to' address to organization to_match = re.search('([\w\-\.]+)@\w[\w\-]+\.+[\w\-]+', mail['To']) if to_match: self._print_if_verbose(verbosity, "Found email 'to' '%s'" % to_match.group(1)) try: organization = Organization.objects.get(slug=to_match.group(1)) org_found = True self._print_if_verbose(verbosity, "Matched email to organization '%s'" % organization.name) except: pass proposal_found = re.search('proposal', mail['Subject'], re.IGNORECASE) #handle multipart mails, cycle through mail #until find text type with a full payload. if mail.is_multipart(): for message in mail.get_payload(): if message.get_content_maintype() == 'text': msg_string = self._strip_string(message.get_payload(), verbosity) if msg_string: break else: msg_string = self._strip_string(mail.get_payload(), verbosity) if user_found and msg_string: if object_found: parse_feedback = re.match('(\w+)\s*:\s*(\w+[\s\w]*)', msg_string, re.IGNORECASE) if parse_feedback: description = parse_feedback.group(2) rating_match = re.match('question|danger|concerns|consent|comment', parse_feedback.group(1), re.IGNORECASE) else: rating_match = None description = msg_string if rating_match: self._print_if_verbose(verbosity, "Found feedback rating '%s'" % rating_match.group()) rating = rating_int(rating_match.group().lower()) else: rating = Feedback.COMMENT_STATUS self._print_if_verbose(verbosity, "Creating feedback with rating '%s' and description '%s'." % (rating, description)) feedback = Feedback(author=user, decision=decision, rating=rating, description=description) feedback.save() elif proposal_found and org_found: self._print_if_verbose(verbosity, "No matching object, creating proposal") if organization in Organization.active.get_for_user(user): decision = Decision(author=user, editor=user, status=Decision.PROPOSAL_STATUS, organization=organization, description=msg_string) decision.save() else: self._print_if_verbose(verbosity, "User %s is not a member of Organization %s" % (user.username, organization.name))