def save(self, **kw):
# If description modified, update the timestamp field.
if self._init_description != self.description:
- self.description_modified = int(time.time())
+ self.description_modified = time.time()
# if last_activity is null (i.e. we are creating the room) set it to now.
if not self.last_activity:
- self.last_activity = int(time.time())
+ self.last_activity = time.time()
if not self.created:
self.created = datetime.datetime.now()
super(Room, self).save(**kw)
- a unix timestamp.
- a datetime timestamp.
The reason: the unix timestamp is higher performance when sending data to the browser (easier
- and faster to handle INTs instead of datetimes. The 'created' is used for displaying the date
+ and faster to handle numbers instead of datetimes. The 'created' is used for displaying the date
of messages; I could calculate it from the unix timestamp, but I'm guessing that I will get
higher performance by storing it in the database.
room = models.ForeignKey(Room, help_text='This message was posted in a given chat room.')
event = models.IntegerField(null=True, blank=True, choices=EVENT_CHOICES, help_text='An action performed in the room, either by a user or by the system (e.g. XYZ leaves room.')
text = models.TextField(null=True, blank=True, help_text='A message, either typed in by a user or generated by the system.')
- unix_timestamp = models.IntegerField(editable=False, help_text='Unix timestamp when this message was inserted into the database.')
+ unix_timestamp = models.FloatField(editable=False, help_text='Unix timestamp when this message was inserted into the database.')
created = models.DateTimeField(editable=False)
def __unicode__(self):
def save(self, **kw):
if not self.unix_timestamp:
- self.unix_timestamp = int(time.time())
+ self.unix_timestamp = time.time()
self.created = datetime.datetime.fromtimestamp(self.unix_timestamp)
super(Message, self).save(**kw)
self.room.last_activity = int(time.time())
# Should have a status of 1 (there are new messages).
self.assert_(payload['status'] == 1, payload)
- # The server returns the Unix time, this will be an integer > 0.
+ # The server returns the Unix time, this will be a number > 0.
t = payload['time']
try:
- t = int(t)
+ t = float(t)
except:
- self.assert_(False, 'Time (%s) should be an integer.' % t)
+ self.assert_(False, 'Time (%s) should be a number.' % t)
messages = payload['messages']
self.assert_('<script>' in messages[-1]['text'])
self.assert_('<script>' not in messages[-1]['text'])
+class BehaviourTest(TestCase):
+ """Check out how the chat window behaves in different scenarios."""
+
+ fixtures = ['test_jqchat.json']
+
+ def setUp(self):
+ self.client = Client()
+ self.client.login(username='mickey', password='test')
+
+ self.client2 = Client()
+ self.client2.login(username='donald', password='test')
+
+ def test_simultaneous_messages(self):
+ """Ensure that messages sent by different users at (virtually) the same time are picked up."""
+
+ response = self.client.post('/jqchat/room/1/ajax/', {'time': 0,
+ 'action': 'postmsg',
+ 'message': 'rhubarb'})
+ self.assert_(response.status_code == 200, response.status_code)
+
+ payload = simplejson.loads(response.content)
+ # Should have a status of 1 (there are new messages).
+ self.assert_(payload['status'] == 1, payload)
+
+ mickey_time = payload['time']
+ messages = payload['messages']
+
+ self.assert_(len(messages) == 5)
+ self.assert_('mickey' in messages[-1]['text'])
+ self.assert_('rhubarb' in messages[-1]['text'])
+
+ # Donald also sends a message, at virtually the same time.
+ response = self.client2.post('/jqchat/room/1/ajax/', {'time': 0,
+ 'action': 'postmsg',
+ 'message': 'crumble'})
+ self.assert_(response.status_code == 200, response.status_code)
+
+ payload = simplejson.loads(response.content)
+ # Should have a status of 1 (there are new messages).
+ self.assert_(payload['status'] == 1, payload)
+
+ messages = payload['messages']
+
+ # Will pick up the message by Mickey, as well as the one just posted by Donald.
+ self.assert_(len(messages) == 6, messages)
+ self.assert_('donald' in messages[-1]['text'])
+ self.assert_('crumble' in messages[-1]['text'])
+
+ # And the next to last message is the one by Mickey.
+ self.assert_('mickey' in messages[-2]['text'])
+ self.assert_('rhubarb' in messages[-2]['text'])
+
+ # Mickey immediately requests the latest messages (it could happen...).
+ # Note how the time is no longer 0 - it's whatever time was returned from the
+ # last AJAX query.
+ response = self.client.get('/jqchat/room/1/ajax/', {'time': mickey_time})
+ self.assert_(response.status_code == 200, response.status_code)
+
+ payload = simplejson.loads(response.content)
+ # Should have a status of 1 (there are new messages).
+ self.assert_(payload['status'] == 1, payload)
+
+ messages = payload['messages']
+
+ # Since Mickey last checked, there has been one message posted by Donald.
+ self.assert_(len(messages) == 1, messages)
+ self.assert_('donald' in messages[-1]['text'])
+ self.assert_('crumble' in messages[-1]['text'])
class EventTest(TestCase):
"""Create new events in the room."""
StatusCode = 0 # Default status code is 0 i.e. no new data.
self.request = request
try:
- self.request_time = int(self.request.REQUEST['time'])
+ self.request_time = float(self.request.REQUEST['time'])
except:
return HttpResponseBadRequest("What's the time?")
self.ThisRoom = Room.objects.get(id=id)
l = len(NewMessages)
if l > JQCHAT_DISPLAY_COUNT:
NewMessages = NewMessages[l-JQCHAT_DISPLAY_COUNT:]
-
+
response = render_to_response('jqchat/chat_payload.json',
- {'current_unix_timestamp': int(time.time()),
+ {'current_unix_timestamp': time.time(),
'NewMessages': NewMessages,
'StatusCode': StatusCode,
'NewDescription': NewDescription,