Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions kitsune/notifications/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,8 @@ def test_correct_fields(self):
'avatar': profile_avatar(followed.user),
})
eq_(serializer.data['verb'], 'asked')
eq_(serializer.data['action_object'], {
'type': 'question',
'id': q.id,
})
eq_(serializer.data['action_object']['type'], 'question')
eq_(serializer.data['action_object']['id'], q.id)
eq_(serializer.data['target'], None)
eq_(type(serializer.data['timestamp']), datetime)

Expand Down
26 changes: 24 additions & 2 deletions kitsune/questions/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ def validate_creator(self, attrs, source):
return attrs


class QuestionFKSerializer(QuestionSerializer):

class Meta:
model = Question
fields = (
'creator',
'id',
'title',
)


class QuestionFilter(django_filters.FilterSet):
product = django_filters.CharFilter(name='product__slug')
creator = django_filters.CharFilter(name='creator__username')
Expand Down Expand Up @@ -249,13 +260,13 @@ def helpful(self, request, pk=None):
@action(methods=['POST'], permission_classes=[permissions.IsAuthenticated])
def follow(self, request, pk=None):
question = self.get_object()
actstream.actions.follow(request.user, question, actor_only=False)
actstream.actions.follow(request.user, question, actor_only=False, send_action=False)
return Response('', status=204)

@action(methods=['POST'], permission_classes=[permissions.IsAuthenticated])
def unfollow(self, request, pk=None):
question = self.get_object()
actstream.actions.unfollow(request.user, question)
actstream.actions.unfollow(request.user, question, send_action=False)
return Response('', status=204)

@action(methods=['POST'])
Expand Down Expand Up @@ -367,6 +378,17 @@ def validate_creator(self, attrs, source):
return attrs


class AnswerFKSerializer(AnswerSerializer):

class Meta:
model = Answer
fields = (
'id',
'question',
'creator',
)


class AnswerFilter(django_filters.FilterSet):
creator = django_filters.CharFilter(name='creator__username')
question = django_filters.Filter(name='question__id')
Expand Down
14 changes: 14 additions & 0 deletions kitsune/questions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,12 @@ def my_tags(self):
def get_mapping_type(cls):
return QuestionMappingType

@classmethod
def get_generic_fk_serializer(cls):
# Avoid circular import
from kitsune.questions.api import QuestionFKSerializer
return QuestionFKSerializer

@classmethod
def recent_asked_count(cls, extra_filter=None):
"""Returns the number of questions asked in the last 24 hours."""
Expand Down Expand Up @@ -478,6 +484,8 @@ def set_solution(self, answer, solver):
self.add_metadata(solver_id=str(solver.id))
statsd.incr('questions.solution')
QuestionSolvedEvent(answer).fire(exclude=self.creator)
actstream.action.send(
solver, verb='marked as a solution', action_object=answer, target=self)

@property
def related_documents(self):
Expand Down Expand Up @@ -1113,6 +1121,12 @@ def get_images(self):
def get_mapping_type(cls):
return AnswerMetricsMappingType

@classmethod
def get_generic_fk_serializer(cls):
# Avoid circular import
from kitsune.questions.api import AnswerFKSerializer
return AnswerFKSerializer

def mark_as_spam(self, by_user):
"""Mark the answer as spam by the specified user."""
self.is_spam = True
Expand Down
22 changes: 17 additions & 5 deletions kitsune/questions/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,8 @@ def test_add_metadata_over_1000_chars(self):
eq_('a'*1000, metadata.value)


class TestSignals(TestCase):
class TestActions(TestCase):

def test_question_create_action(self):
"""When a question is created, an Action is created too."""
q = question(save=True)
Expand All @@ -622,15 +623,26 @@ def test_answer_create_action(self):
eq_(act.target, q)

def test_question_change_no_action(self):
"""When a question is changed, no action should be created."""
"""When a question is changed, no Action should be created."""
q = question(save=True)
Action.objects.all().delete()
q.save() # trigger another post_save hook
eq_(Action.objects.count(), 0)

def test_answer_change_no_action(self):
"""When an answer is changed, no action should be created."""
a = question(save=True)
"""When an answer is changed, no Action should be created."""
q = question(save=True)
Action.objects.all().delete()
a.save() # trigger another post_save hook
q.save() # trigger another post_save hook
eq_(Action.objects.count(), 0)

def test_question_solved_makes_action(self):
"""When an answer is marked as the solution to a question, an Action should be created."""
ans = answer(save=True)
Action.objects.all().delete()
ans.question.set_solution(ans, ans.question.creator)

act = Action.objects.action_object(ans).get()
eq_(act.actor, ans.question.creator)
eq_(act.verb, 'marked as a solution')
eq_(act.target, ans.question)