muting: Record DateTime when a Topic is muted.

This includes the necessary migration to add
the date_muted field to the MutedTopic class
and populates it with a hard coded value.
This commit is contained in:
Ryan Rehman
2020-01-17 20:31:00 +05:30
committed by Tim Abbott
parent e4259d48a5
commit 3dc7d60ffe
8 changed files with 65 additions and 7 deletions

View File

@@ -5286,8 +5286,11 @@ def do_set_alert_words(user_profile: UserProfile, alert_words: List[str]) -> Non
set_user_alert_words(user_profile, alert_words)
notify_alert_words(user_profile, alert_words)
def do_mute_topic(user_profile: UserProfile, stream: Stream, recipient: Recipient, topic: str) -> None:
add_topic_mute(user_profile, stream.id, recipient.id, topic)
def do_mute_topic(user_profile: UserProfile, stream: Stream, recipient: Recipient, topic: str,
date_muted: Optional[datetime.datetime]=None) -> None:
if date_muted is None:
date_muted = timezone_now()
add_topic_mute(user_profile, stream.id, recipient.id, topic, date_muted)
event = dict(type="muted_topics", muted_topics=get_topic_mutes(user_profile))
send_event(user_profile.realm, event, [user_profile.id])

View File

@@ -247,6 +247,7 @@ ANALYTICS_TABLES = {
DATE_FIELDS = {
'zerver_attachment': ['create_time'],
'zerver_message': ['last_edit_time', 'date_sent'],
'zerver_mutedtopic': ['date_muted'],
'zerver_realm': ['date_created'],
'zerver_stream': ['date_created'],
'zerver_useractivity': ['last_visit'],

View File

@@ -914,6 +914,7 @@ def do_import_realm(import_dir: Path, subdomain: str, processes: int=1) -> Realm
bulk_import_model(data, UserHotspot)
if 'zerver_mutedtopic' in data:
fix_datetime_fields(data, 'zerver_mutedtopic')
re_map_foreign_keys(data, 'zerver_mutedtopic', 'user_profile', related_table='user_profile')
re_map_foreign_keys(data, 'zerver_mutedtopic', 'stream', related_table='stream')
re_map_foreign_keys(data, 'zerver_mutedtopic', 'recipient', related_table='recipient')

View File

@@ -1,4 +1,5 @@
from typing import Any, Callable, Dict, List, Optional
import datetime
from zerver.lib.topic import (
topic_match_sa,
@@ -17,6 +18,8 @@ from sqlalchemy.sql import (
Selectable
)
from django.utils.timezone import now as timezone_now
def get_topic_mutes(user_profile: UserProfile) -> List[List[str]]:
rows = MutedTopic.objects.filter(
user_profile=user_profile,
@@ -29,7 +32,8 @@ def get_topic_mutes(user_profile: UserProfile) -> List[List[str]]:
for row in rows
]
def set_topic_mutes(user_profile: UserProfile, muted_topics: List[List[str]]) -> None:
def set_topic_mutes(user_profile: UserProfile, muted_topics: List[List[str]],
date_muted: Optional[datetime.datetime]=None) -> None:
'''
This is only used in tests.
@@ -39,6 +43,8 @@ def set_topic_mutes(user_profile: UserProfile, muted_topics: List[List[str]]) ->
user_profile=user_profile,
).delete()
if date_muted is None:
date_muted = timezone_now()
for stream_name, topic_name in muted_topics:
stream = get_stream(stream_name, user_profile.realm)
recipient = get_stream_recipient(stream.id)
@@ -48,14 +54,19 @@ def set_topic_mutes(user_profile: UserProfile, muted_topics: List[List[str]]) ->
stream_id=stream.id,
recipient_id=recipient.id,
topic_name=topic_name,
date_muted=date_muted,
)
def add_topic_mute(user_profile: UserProfile, stream_id: int, recipient_id: int, topic_name: str) -> None:
def add_topic_mute(user_profile: UserProfile, stream_id: int, recipient_id: int, topic_name: str,
date_muted: Optional[datetime.datetime]=None) -> None:
if date_muted is None:
date_muted = timezone_now()
MutedTopic.objects.create(
user_profile=user_profile,
stream_id=stream_id,
recipient_id=recipient_id,
topic_name=topic_name,
date_muted=date_muted,
)
def remove_topic_mute(user_profile: UserProfile, stream_id: int, topic_name: str) -> None:

View File

@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.26 on 2020-01-17 15:26
from __future__ import unicode_literals
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('zerver', '0261_realm_private_message_policy'),
]
operations = [
migrations.AddField(
model_name='mutedtopic',
name='date_muted',
field=models.DateTimeField(default=datetime.datetime(2020, 1, 1, 0, 0)),
),
]

View File

@@ -1457,12 +1457,19 @@ class MutedTopic(models.Model):
stream = models.ForeignKey(Stream, on_delete=CASCADE)
recipient = models.ForeignKey(Recipient, on_delete=CASCADE)
topic_name = models.CharField(max_length=MAX_TOPIC_NAME_LENGTH)
# The default value for date_muted is a few weeks before tracking
# of when topics were muted was first introduced. It's designed
# to be obviously incorrect so that users can tell it's backfilled data.
date_muted = models.DateTimeField(default=datetime.datetime(2020, 1, 1, 0, 0, 0, 0))
class Meta:
unique_together = ('user_profile', 'stream', 'topic_name')
def __str__(self) -> str:
return "<MutedTopic: (%s, %s, %s)>" % (self.user_profile.email, self.stream.name, self.topic_name)
return ("<MutedTopic: (%s, %s, %s, %s)>" % (self.user_profile.email,
self.stream.name,
self.topic_name,
self.date_muted))
class Client(models.Model):
name = models.CharField(max_length=30, db_index=True, unique=True) # type: str

View File

@@ -1,3 +1,5 @@
from django.utils.timezone import now as timezone_now
from datetime import timedelta
from typing import Any, Dict
from zerver.lib.test_classes import ZulipTestCase
@@ -7,6 +9,7 @@ from zerver.models import (
get_stream,
get_stream_recipient,
UserProfile,
MutedTopic
)
from zerver.lib.topic_mutes import (
@@ -39,15 +42,20 @@ class MutedTopicsTests(ZulipTestCase):
stream_id=stream.id,
recipient_id=recipient.id,
topic_name='test TOPIC',
date_muted=timezone_now(),
)
mute_user(hamlet)
user_ids = stream_topic_target.user_ids_muting_topic()
self.assertEqual(user_ids, {hamlet.id})
hamlet_date_muted = MutedTopic.objects.filter(user_profile=hamlet)[0].date_muted
self.assertTrue(timezone_now() - hamlet_date_muted <= timedelta(seconds=100))
mute_user(cordelia)
user_ids = stream_topic_target.user_ids_muting_topic()
self.assertEqual(user_ids, {hamlet.id, cordelia.id})
cordelia_date_muted = MutedTopic.objects.filter(user_profile=cordelia)[0].date_muted
self.assertTrue(timezone_now() - cordelia_date_muted <= timedelta(seconds=100))
def test_add_muted_topic(self) -> None:
user = self.example_user('hamlet')
@@ -98,6 +106,7 @@ class MutedTopicsTests(ZulipTestCase):
stream_id=stream.id,
recipient_id=recipient.id,
topic_name='Verona3',
date_muted=timezone_now(),
)
self.assertIn([stream.name, 'Verona3'], get_topic_mutes(user))
@@ -120,6 +129,7 @@ class MutedTopicsTests(ZulipTestCase):
stream_id=stream.id,
recipient_id=recipient.id,
topic_name=u'Verona3',
date_muted=timezone_now(),
)
url = '/api/v1/users/me/subscriptions/muted_topics'

View File

@@ -1,6 +1,8 @@
from django.http import HttpResponse, HttpRequest
from typing import Optional
import datetime
from django.utils.timezone import now as timezone_now
from django.utils.translation import ugettext as _
from zerver.lib.actions import do_mute_topic, do_unmute_topic
from zerver.lib.request import has_request_variables, REQ
@@ -19,7 +21,8 @@ from zerver.models import UserProfile
def mute_topic(user_profile: UserProfile,
stream_id: Optional[int],
stream_name: Optional[str],
topic_name: str) -> HttpResponse:
topic_name: str,
date_muted: datetime.datetime) -> HttpResponse:
if stream_name is not None:
(stream, recipient, sub) = access_stream_by_name(user_profile, stream_name)
else:
@@ -29,7 +32,7 @@ def mute_topic(user_profile: UserProfile,
if topic_is_muted(user_profile, stream.id, topic_name):
return json_error(_("Topic already muted"))
do_mute_topic(user_profile, stream, recipient, topic_name)
do_mute_topic(user_profile, stream, recipient, topic_name, date_muted)
return json_success()
def unmute_topic(user_profile: UserProfile,
@@ -66,6 +69,7 @@ def update_muted_topic(request: HttpRequest,
stream_id=stream_id,
stream_name=stream,
topic_name=topic,
date_muted=timezone_now(),
)
elif op == 'remove':
return unmute_topic(