retention: Add models to store expired messages.

This addresses part of #106.
This commit is contained in:
K.Kanakhin 2016-11-01 16:26:38 +06:00 committed by Tim Abbott
parent 18e66983c4
commit fe3213798d
2 changed files with 142 additions and 15 deletions

View File

@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-26 01:10
from __future__ import unicode_literals
import bitfield.models
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import zerver.lib.str_utils
class Migration(migrations.Migration):
dependencies = [
('zerver', '0066_realm_inline_url_embed_preview'),
]
operations = [
migrations.CreateModel(
name='ArchivedAttachment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('file_name', models.TextField(db_index=True)),
('path_id', models.TextField(db_index=True)),
('is_realm_public', models.BooleanField(default=False)),
('create_time', models.DateTimeField(db_index=True, default=django.utils.timezone.now)),
('size', models.IntegerField(null=True)),
('archive_timestamp', models.DateTimeField(db_index=True, default=django.utils.timezone.now)),
],
options={
'abstract': False,
},
bases=(zerver.lib.str_utils.ModelReprMixin, models.Model),
),
migrations.CreateModel(
name='ArchivedMessage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('subject', models.CharField(db_index=True, max_length=60)),
('content', models.TextField()),
('rendered_content', models.TextField(null=True)),
('rendered_content_version', models.IntegerField(null=True)),
('pub_date', models.DateTimeField(db_index=True, verbose_name='date published')),
('last_edit_time', models.DateTimeField(null=True)),
('edit_history', models.TextField(null=True)),
('has_attachment', models.BooleanField(db_index=True, default=False)),
('has_image', models.BooleanField(db_index=True, default=False)),
('has_link', models.BooleanField(db_index=True, default=False)),
('archive_timestamp', models.DateTimeField(db_index=True, default=django.utils.timezone.now)),
('recipient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='zerver.Recipient')),
('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('sending_client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='zerver.Client')),
],
options={
'abstract': False,
},
bases=(zerver.lib.str_utils.ModelReprMixin, models.Model),
),
migrations.CreateModel(
name='ArchivedUserMessage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('flags', bitfield.models.BitField(['read', 'starred', 'collapsed', 'mentioned', 'wildcard_mentioned', 'summarize_in_home', 'summarize_in_stream', 'force_expand', 'force_collapse', 'has_alert_word', 'historical', 'is_me_message'], default=0)),
('archive_timestamp', models.DateTimeField(db_index=True, default=django.utils.timezone.now)),
('message', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='zerver.ArchivedMessage')),
('user_profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
bases=(zerver.lib.str_utils.ModelReprMixin, models.Model),
),
migrations.AddField(
model_name='archivedattachment',
name='messages',
field=models.ManyToManyField(to='zerver.ArchivedMessage'),
),
migrations.AddField(
model_name='archivedattachment',
name='owner',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='archivedattachment',
name='realm',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='zerver.Realm'),
),
migrations.AlterUniqueTogether(
name='archivedusermessage',
unique_together=set([('user_profile', 'message')]),
),
]

View File

@ -950,7 +950,7 @@ def sew_messages_and_reactions(messages, reactions):
return list(converted_messages.values())
class Message(ModelReprMixin, models.Model):
class AbstractMessage(ModelReprMixin, models.Model):
sender = models.ForeignKey(UserProfile) # type: UserProfile
recipient = models.ForeignKey(Recipient) # type: Recipient
subject = models.CharField(max_length=MAX_SUBJECT_LENGTH, db_index=True) # type: Text
@ -965,6 +965,16 @@ class Message(ModelReprMixin, models.Model):
has_image = models.BooleanField(default=False, db_index=True) # type: bool
has_link = models.BooleanField(default=False, db_index=True) # type: bool
class Meta(object):
abstract = True
class ArchivedMessage(AbstractMessage):
archive_timestamp = models.DateTimeField(default=timezone.now, db_index=True) # type: datetime.datetime
class Message(AbstractMessage):
def topic_name(self):
# type: () -> Text
"""
@ -1139,9 +1149,8 @@ class Reaction(ModelReprMixin, models.Model):
#
# UserMessage is the largest table in a Zulip installation, even
# though each row is only 4 integers.
class UserMessage(ModelReprMixin, models.Model):
class AbstractUserMessage(ModelReprMixin, models.Model):
user_profile = models.ForeignKey(UserProfile) # type: UserProfile
message = models.ForeignKey(Message) # type: Message
# We're not using the archived field for now, but create it anyway
# since this table will be an unpleasant one to do schema changes
# on later
@ -1151,16 +1160,27 @@ class UserMessage(ModelReprMixin, models.Model):
flags = BitField(flags=ALL_FLAGS, default=0) # type: BitHandler
class Meta(object):
abstract = True
unique_together = ("user_profile", "message")
def flags_list(self):
# type: () -> List[str]
return [flag for flag in self.flags.keys() if getattr(self.flags, flag).is_set]
class ArchivedUserMessage(AbstractUserMessage):
message = models.ForeignKey(ArchivedMessage) # type: Message
archive_timestamp = models.DateTimeField(default=timezone.now, db_index=True) # type: datetime.datetime
class UserMessage(AbstractUserMessage):
message = models.ForeignKey(Message) # type: Message
def __unicode__(self):
# type: () -> Text
display_recipient = get_display_recipient(self.message.recipient)
return u"<UserMessage: %s / %s (%s)>" % (display_recipient, self.user_profile.email, self.flags_list())
def flags_list(self):
# type: () -> List[str]
return [flag for flag in self.flags.keys() if getattr(self.flags, flag).is_set]
def parse_usermessage_flags(val):
# type: (int) -> List[str]
@ -1172,18 +1192,31 @@ def parse_usermessage_flags(val):
mask <<= 1
return flags
class Attachment(ModelReprMixin, models.Model):
file_name = models.TextField(db_index=True) # type: Text
class AbstractAttachment(ModelReprMixin, models.Model):
file_name = models.TextField(db_index=True) # type: Text
# path_id is a storage location agnostic representation of the path of the file.
# If the path of a file is http://localhost:9991/user_uploads/a/b/abc/temp_file.py
# then its path_id will be a/b/abc/temp_file.py.
path_id = models.TextField(db_index=True) # type: Text
owner = models.ForeignKey(UserProfile) # type: UserProfile
realm = models.ForeignKey(Realm, blank=True, null=True) # type: Realm
is_realm_public = models.BooleanField(default=False) # type: bool
messages = models.ManyToManyField(Message) # type: Manager
create_time = models.DateTimeField(default=timezone.now, db_index=True) # type: datetime.datetime
size = models.IntegerField(null=True) # type: int
path_id = models.TextField(db_index=True) # type: Text
owner = models.ForeignKey(UserProfile) # type: UserProfile
realm = models.ForeignKey(Realm, blank=True, null=True) # type: Realm
is_realm_public = models.BooleanField(default=False) # type: bool
create_time = models.DateTimeField(default=timezone.now,
db_index=True) # type: datetime.datetime
size = models.IntegerField(null=True) # type: int
class Meta(object):
abstract = True
class ArchivedAttachment(AbstractAttachment):
archive_timestamp = models.DateTimeField(default=timezone.now, db_index=True) # type: datetime.datetime
messages = models.ManyToManyField(ArchivedMessage) # type: Manager
class Attachment(AbstractAttachment):
messages = models.ManyToManyField(Message) # type: Manager
def __unicode__(self):
# type: () -> Text
@ -1207,6 +1240,7 @@ class Attachment(ModelReprMixin, models.Model):
} for m in self.messages.all()]
}
def get_old_unclaimed_attachments(weeks_ago):
# type: (int) -> Sequence[Attachment]
# TODO: Change return type to QuerySet[Attachment]