diff --git a/zephyr/lib/utils.py b/zephyr/lib/utils.py new file mode 100644 index 0000000000..d3ba9c2399 --- /dev/null +++ b/zephyr/lib/utils.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from time import sleep + +# Runs the callback with slices of all_list of a given batch_size +def run_in_batches(all_list, batch_size, callback, sleep_time = 0, logger = None): + if len(all_list) == 0: + return + + limit = (len(all_list) / batch_size) + 1; + for i in xrange(limit): + start = i*batch_size + end = (i+1) * batch_size + if end >= len(all_list): + end = len(all_list) + batch = all_list[start:end] + + if logger: + logger("Executing %s in batch %s of %s" % (end-start, i+1, limit)) + + callback(batch) + sleep(sleep_time) diff --git a/zephyr/migrations/0012_auto__add_field_usermessage_flags.py b/zephyr/migrations/0012_auto__add_field_usermessage_flags.py new file mode 100644 index 0000000000..7cf47cdaaf --- /dev/null +++ b/zephyr/migrations/0012_auto__add_field_usermessage_flags.py @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- +import datetime +import logging +from south.db import db +from south.v2 import SchemaMigration +from django.db import models, transaction, connection + +from zephyr.lib import utils + +class Migration(SchemaMigration): + + def forwards(self, orm): + cursor = connection.cursor() + cursor.execute("ALTER TABLE zephyr_usermessage ADD COLUMN flags bigint;") + + cursor.execute(""" +CREATE FUNCTION set_flags_trigger() RETURNS TRIGGER AS $set_flags_trigger$ + BEGIN + NEW.flags := 0; + RETURN NEW; + END; +$set_flags_trigger$ LANGUAGE plpgsql; + +CREATE TRIGGER set_flags_trigger BEFORE INSERT ON zephyr_usermessage + FOR EACH ROW EXECUTE PROCEDURE set_flags_trigger(); +""") + transaction.commit_unless_managed() + + for user_profile in orm.UserProfile.objects.all(): + msgs = [m.id for m in orm.UserMessage.objects.filter(user_profile=user_profile).order_by('id')] + + def update_batch(batch): + with transaction.commit_on_success(): + orm.UserMessage.objects.filter(id__in=batch) \ + .update(flags=0) + # Batch in set of 5000 + utils.run_in_batches(msgs, 5000, update_batch, sleep_time=1, + logger=logging.info) + + cursor.execute("ALTER TABLE zephyr_usermessage ALTER COLUMN flags SET NOT NULL;") + cursor.execute("ALTER TABLE zephyr_usermessage ALTER COLUMN flags SET DEFAULT 0;") + cursor.execute("DROP TRIGGER set_flags_trigger ON zephyr_usermessage;") + cursor.execute("DROP FUNCTION set_flags_trigger();") + transaction.commit_unless_managed() + + def backwards(self, orm): + # Deleting field 'UserMessage.flags' + db.delete_column('zephyr_usermessage', 'flags') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'zephyr.client': { + 'Meta': {'object_name': 'Client'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30', 'db_index': 'True'}) + }, + 'zephyr.defaultstream': { + 'Meta': {'unique_together': "(('realm', 'stream'),)", 'object_name': 'DefaultStream'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Realm']"}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Stream']"}) + }, + 'zephyr.huddle': { + 'Meta': {'object_name': 'Huddle'}, + 'huddle_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'zephyr.message': { + 'Meta': {'object_name': 'Message'}, + 'content': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'pub_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'recipient': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Recipient']"}), + 'sender': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"}), + 'sending_client': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Client']"}), + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '60', 'db_index': 'True'}) + }, + 'zephyr.mituser': { + 'Meta': {'object_name': 'MitUser'}, + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'zephyr.preregistrationuser': { + 'Meta': {'object_name': 'PreregistrationUser'}, + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invited_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'referred_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']", 'null': 'True'}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'streams': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['zephyr.Stream']", 'null': 'True', 'symmetrical': 'False'}) + }, + 'zephyr.realm': { + 'Meta': {'object_name': 'Realm'}, + 'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'restricted_to_domain': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'zephyr.recipient': { + 'Meta': {'unique_together': "(('type', 'type_id'),)", 'object_name': 'Recipient'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'db_index': 'True'}), + 'type_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'zephyr.stream': { + 'Meta': {'unique_together': "(('name', 'realm'),)", 'object_name': 'Stream'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invite_only': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'db_index': 'True'}), + 'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Realm']"}) + }, + 'zephyr.streamcolor': { + 'Meta': {'object_name': 'StreamColor'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '10'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'subscription': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Subscription']"}) + }, + 'zephyr.subscription': { + 'Meta': {'unique_together': "(('user_profile', 'recipient'),)", 'object_name': 'Subscription'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_home_view': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}), + 'recipient': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Recipient']"}), + 'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"}) + }, + 'zephyr.useractivity': { + 'Meta': {'unique_together': "(('user_profile', 'client', 'query'),)", 'object_name': 'UserActivity'}, + 'client': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Client']"}), + 'count': ('django.db.models.fields.IntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_visit': ('django.db.models.fields.DateTimeField', [], {}), + 'query': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"}) + }, + 'zephyr.usermessage': { + 'Meta': {'unique_together': "(('user_profile', 'message'),)", 'object_name': 'UserMessage'}, + 'archived': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'flags': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Message']"}), + 'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"}) + }, + 'zephyr.userpresence': { + 'Meta': {'unique_together': "(('user_profile', 'client'),)", 'object_name': 'UserPresence'}, + 'client': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Client']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {}), + 'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"}) + }, + 'zephyr.userprofile': { + 'Meta': {'object_name': 'UserProfile'}, + 'api_key': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'enable_desktop_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'enter_sends': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}), + 'full_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_pointer_updater': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'pointer': ('django.db.models.fields.IntegerField', [], {}), + 'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Realm']"}), + 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}) + } + } + + complete_apps = ['zephyr'] \ No newline at end of file diff --git a/zephyr/migrations/0013_usermessage_unreadcount.py b/zephyr/migrations/0013_usermessage_unreadcount.py new file mode 100644 index 0000000000..54d23b38c2 --- /dev/null +++ b/zephyr/migrations/0013_usermessage_unreadcount.py @@ -0,0 +1,199 @@ +# -*- coding: utf-8 -*- +import datetime +import logging + +from south.db import db +from south.v2 import DataMigration +from django.db import models, transaction, connection +from django.conf import settings + +from zephyr.lib import utils + +class Migration(DataMigration): + + def forwards(self, orm): + # Mark all messages not sent by this user, but past her saved pointer, + # as unread. + for user_profile in orm.UserProfile.objects.all(): + pointer = user_profile.pointer + if pointer == -1: + try: + pointer = (orm.UserMessage.objects.filter(user_profile=user_profile) + .order_by('message') + .reverse()[0]).message_id + except IndexError: + pass + + # We have to use the `long` representation of the unread bit, + # as we don't have access to BitHandler or BitFields here. + # + # 'read' is the first bit in flags + msgs = [m.id for m in orm.UserMessage.objects.filter(user_profile=user_profile, + message_id__lte=pointer) + .exclude(flags=1)] + + def update_batch(batch): + with transaction.commit_on_success(): + orm.UserMessage.objects.filter(id__in=batch) \ + .update(flags=1) + + # Batch in set of 5000 + utils.run_in_batches(msgs, 5000, update_batch, sleep_time=1, + logger=logging.info) + + def backwards(self, orm): + "Write your backwards methods here." + # Nothing to do, flags field is dropped in schema migration + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'zephyr.client': { + 'Meta': {'object_name': 'Client'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30', 'db_index': 'True'}) + }, + 'zephyr.defaultstream': { + 'Meta': {'unique_together': "(('realm', 'stream'),)", 'object_name': 'DefaultStream'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Realm']"}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Stream']"}) + }, + 'zephyr.huddle': { + 'Meta': {'object_name': 'Huddle'}, + 'huddle_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'zephyr.message': { + 'Meta': {'object_name': 'Message'}, + 'content': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'pub_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'recipient': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Recipient']"}), + 'sender': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"}), + 'sending_client': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Client']"}), + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '60', 'db_index': 'True'}) + }, + 'zephyr.mituser': { + 'Meta': {'object_name': 'MitUser'}, + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'zephyr.preregistrationuser': { + 'Meta': {'object_name': 'PreregistrationUser'}, + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invited_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'referred_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']", 'null': 'True'}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'streams': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['zephyr.Stream']", 'null': 'True', 'symmetrical': 'False'}) + }, + 'zephyr.realm': { + 'Meta': {'object_name': 'Realm'}, + 'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'restricted_to_domain': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'zephyr.recipient': { + 'Meta': {'unique_together': "(('type', 'type_id'),)", 'object_name': 'Recipient'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'db_index': 'True'}), + 'type_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'zephyr.stream': { + 'Meta': {'unique_together': "(('name', 'realm'),)", 'object_name': 'Stream'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'invite_only': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'db_index': 'True'}), + 'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Realm']"}) + }, + 'zephyr.streamcolor': { + 'Meta': {'object_name': 'StreamColor'}, + 'color': ('django.db.models.fields.CharField', [], {'max_length': '10'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'subscription': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Subscription']"}) + }, + 'zephyr.subscription': { + 'Meta': {'unique_together': "(('user_profile', 'recipient'),)", 'object_name': 'Subscription'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_home_view': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}), + 'recipient': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Recipient']"}), + 'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"}) + }, + 'zephyr.useractivity': { + 'Meta': {'unique_together': "(('user_profile', 'client', 'query'),)", 'object_name': 'UserActivity'}, + 'client': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Client']"}), + 'count': ('django.db.models.fields.IntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_visit': ('django.db.models.fields.DateTimeField', [], {}), + 'query': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"}) + }, + 'zephyr.usermessage': { + 'Meta': {'unique_together': "(('user_profile', 'message'),)", 'object_name': 'UserMessage'}, + 'flags': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Message']"}), + 'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"}) + }, + 'zephyr.userpresence': { + 'Meta': {'unique_together': "(('user_profile', 'client'),)", 'object_name': 'UserPresence'}, + 'client': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Client']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {}), + 'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.UserProfile']"}) + }, + 'zephyr.userprofile': { + 'Meta': {'object_name': 'UserProfile'}, + 'api_key': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'enable_desktop_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'enter_sends': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}), + 'full_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_pointer_updater': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'pointer': ('django.db.models.fields.IntegerField', [], {}), + 'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['zephyr.Realm']"}), + 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}) + } + } + + complete_apps = ['zephyr'] + symmetrical = True