예제 #1
0
 def setUp(self):
     Segment.helper = SegmentHelper(redis_obj=fakeredis.FakeStrictRedis(
         charset='utf-8', decode_responses=True))
     self.u = UserFactory()
     self.s = AllUserSegmentFactory()
     app_settings.SEGMENTS_REFRESH_ASYNC = False
     app_settings.SEGMENTS_REFRESH_ON_SAVE = True
예제 #2
0
 def setUp(self):
     helper = SegmentHelper(redis_obj=fakeredis.FakeStrictRedis(
         charset='utf-8', decode_responses=True))
     Segment.helper = helper
     helper.redis.flushdb()
예제 #3
0
def delete_segment(segment_id):
    """Celery task to delete an individual Segment from Redis """
    SegmentHelper().delete_segment(segment_id)
예제 #4
0
 def setUp(self):
     self.helper = SegmentHelper()
     self.user = UserFactory()
     SegmentHelper.redis = fakeredis.FakeStrictRedis(charset='utf-8',
                                                     decode_responses=True)
예제 #5
0
class TestSegmentHelper(TestCase):
    def setUp(self):
        self.helper = SegmentHelper()
        self.user = UserFactory()
        SegmentHelper.redis = fakeredis.FakeStrictRedis(charset='utf-8',
                                                        decode_responses=True)

    def test_add_segment_membership(self):
        s = SegmentFactory()
        s.add_member(self.user)
        self.assertTrue(self.helper.segment_has_member(s.id, self.user.id))

    def test_segment_has_member_when_segment_exists(self):
        s = SegmentFactory()
        s.add_member(self.user)
        self.assertTrue(self.helper.segment_has_member(s.id, self.user.id))

    def test_segment_has_member_nonexistant_segment(self):
        s = SegmentFactory()
        s.add_member(self.user)
        self.helper.remove_segment_membership(99999, self.user.id)
        self.assertTrue(self.helper.segment_has_member(s.id, self.user.id))

    def test_remove_segment_membership_segment_exists(self):
        s = SegmentFactory()
        s.add_member(self.user)
        self.helper.remove_segment_membership(s.id, self.user.id)
        self.assertFalse(self.helper.segment_has_member(s.id, self.user.id))

    def test_get_user_segments_when_segment_exists(self):
        s = SegmentFactory()
        s.add_member(self.user)
        segments = self.helper.get_user_segments(self.user.id)
        self.assertTrue(len(segments) > 0)

    def test_get_user_segments_when_invalid_user(self):
        s = SegmentFactory()
        s.add_member(self.user)
        segments = self.helper.get_user_segments(9999)
        self.assertEquals(len(segments), 0)

    def test_get_segment_membership_count(self):
        s = SegmentFactory()
        s.add_member(self.user)
        self.assertEquals(self.helper.get_segment_membership_count(s.id), 1)

    def test_get_segment_members_valid_segment(self):
        s = SegmentFactory()
        s.add_member(self.user)
        members = self.helper.get_segment_members(s.id)
        self.assertEquals(len(list(members)), 1)

    def test_get_segment_members_invalid_segment(self):
        s = SegmentFactory()
        s.add_member(self.user)
        members = self.helper.get_segment_members(99999)
        self.assertEquals(len(list(members)), 0)

    def test_get_refreshed_users(self):
        s = SegmentFactory()
        self.helper.refresh_segment(
            s.id, 'select %s from %s' % (self.user.pk, user_table()))
        self.assertEquals(len(list(self.helper.get_refreshed_users())), 1)

    def test_remove_refreshed_user(self):
        s = SegmentFactory()
        self.helper.refresh_segment(
            s.id, 'select %s from %s' % (self.user.pk, user_table()))
        self.helper.remove_refreshed_user(self.user.id)
        self.assertEquals(len(list(self.helper.get_refreshed_users())), 0)

    def test_refresh_segment_invalid_sql(self):
        s = SegmentFactory()
        invalid_sql = 'abc select '
        self.assertEquals(self.helper.refresh_segment(s.id, invalid_sql), 0)

    def test_refresh_segment_valid_sql(self):
        s = SegmentFactory()
        valid_sql = 'select * from %s' % user_table()
        self.assertEquals(self.helper.refresh_segment(s.id, valid_sql), 1)

    @patch('segments.tasks.delete_segment.delay')
    def test_delete_segment(self, p_delete_segment):
        s = SegmentFactory()
        s.add_member(self.user)
        s.delete()
        self.assertFalse(self.helper.segment_has_member(s.id, self.user.id))
        self.assertTrue(p_delete_segment.called)

    def test_diff_segment(self):
        s1 = SegmentFactory()
        u1 = UserFactory()
        s1.add_member(u1)
        s2 = SegmentFactory()
        u2 = UserFactory()
        s2.add_member(u1)
        s2.add_member(u2)

        s1_key = self.helper.segment_key % s1.id
        s2_key = self.helper.segment_key % s2.id
        self.helper.diff_segment(s2_key, s1_key, 'diff_test')
        self.assertEquals(self.helper.redis.smembers('diff_test'),
                          {str(u2.id)})

    def test_chunk_items(self):
        members = [1, 2, 3]
        for i in members:
            self.assertEquals(
                len(list(chunk_items(members, len(members), i))[0]), i)
        self.assertEquals(len(list(chunk_items([], len(members), 1))[0]), 0)

    def test_raw_user_query(self):
        invalid = ['', None, 12345, "select 'pretendemail'"]
        for i in invalid:
            items, count = execute_raw_user_query(i)
            self.assertEquals(count, 0)
            self.assertEquals(len(items), 0)

        valid_sql = 'select * from %s' % user_table()
        items, count = execute_raw_user_query(valid_sql)
        self.assertEquals(count, 1)
        self.assertEquals(len(items), 1)
예제 #6
0
class Segment(models.Model):
    """
    A segment, as defined by a SQL query. Segments are designed to be stored in Redis and periodically refreshed.
    """

    name = models.CharField(max_length=128)
    slug = models.SlugField(max_length=256, null=True, blank=True, unique=True)
    definition = models.TextField(
        help_text="SQL query returning IDs of users in the segment.",
        blank=True,
        null=True)
    priority = models.PositiveIntegerField(null=True, blank=True)
    members_count = models.PositiveIntegerField(null=True,
                                                blank=True,
                                                default=0)
    created_date = models.DateTimeField(auto_now_add=True)
    updated_date = models.DateTimeField(null=True,
                                        blank=True,
                                        db_index=True,
                                        auto_now=True)
    recalculated_date = models.DateTimeField(null=True, blank=True)

    helper = SegmentHelper()

    ############
    # Public API
    ############

    def has_member(self, user):
        """
        Helper method. Return a bool indicating whether the user is a member of this segment.
        """
        if not user.id:
            return False
        return self.helper.segment_has_member(self.id, user.id)

    def add_member(self, user):
        """ Helper method. Adds member to this segment. Returns a bool indicating the add status """
        if not user.id:
            return False
        return self.helper.add_segment_membership(self.id, user.id)

    @live_sql
    def refresh(self):
        members_count = self.helper.refresh_segment(self.id, self.definition)
        Segment.objects.select_for_update().filter(id=self.id).update(
            members_count=members_count, recalculated_date=timezone.now())
        self.refresh_from_db()

    def __len__(self):
        """Calling len() on a segment returns the number of members of that segment."""
        return self.members_count

    # A lot of code that interfaces with Django models expects model instances to be
    # truthy, to distingiush them from `None`. Since we override `__len__`, `Segment`s
    # with no members will be incorrectly treated as non-existent.
    def __bool__(self):
        return True

    def __str__(self):
        return self.name