class KegSessionChunk(_AbstractChunk): """A specific keg's contribution to a session (spans all users).""" class Meta: unique_together = ('session', 'keg') get_latest_by = 'starttime' ordering = ('-starttime',) objects = managers.SessionManager() site = models.ForeignKey(KegbotSite, related_name='keg_chunks') session = models.ForeignKey(DrinkingSession, related_name='keg_chunks') keg = models.ForeignKey(Keg, related_name='keg_session_chunks', blank=True, null=True) def GetTitle(self): return self.session.GetTitle()
class DrinkingSession(_AbstractChunk): """A collection of contiguous drinks. """ class Meta: unique_together = ('site', 'seqn') get_latest_by = 'start_time' ordering = ('-start_time', ) objects = managers.SessionManager() site = models.ForeignKey(KegbotSite, related_name='sessions') seqn = models.PositiveIntegerField(editable=False) name = models.CharField(max_length=256, blank=True, null=True) slug = AutoSlugField(populate_from='name', unique_with='site', blank=True, null=True) def __str__(self): return "Session #%s: %s" % (self.seqn, self.start_time) def RecomputeStats(self): self.stats.all().delete() try: last_d = self.drinks.valid().latest() last_d._UpdateSessionStats() except Drink.DoesNotExist: pass @models.permalink def get_absolute_url(self): if self.slug: slug = self.slug else: slug = 'session-%i' % self.seqn return ('kb-session', (), { 'kbsite_name': self.site.url(), 'year': self.start_time.year, 'month': '%02i' % self.start_time.month, 'day': '%02i' % self.start_time.day, 'seqn': self.seqn, 'slug': slug }) def GetStatsRecord(self): try: return SessionStats.objects.get(session=self) except SessionStats.DoesNotExist: return None def GetStats(self): record = self.GetStatsRecord() if record: return record.stats return {} def summarize_drinkers(self): def fmt(user): url = '/drinker/%s/' % (user.username, ) return '<a href="%s">%s</a>' % (url, user.username) chunks = self.user_chunks.all().order_by('-volume_ml') users = tuple(c.user for c in chunks) names = tuple(fmt(u) for u in users if u) if None in users: guest_trailer = ' (and possibly others)' else: guest_trailer = '' num = len(names) if num == 0: return 'no known drinkers' elif num == 1: ret = names[0] elif num == 2: ret = '%s and %s' % names elif num == 3: ret = '%s, %s and %s' % names else: if guest_trailer: return '%s, %s and at least %i others' % (names[0], names[1], num - 2) else: return '%s, %s and %i others' % (names[0], names[1], num - 2) return '%s%s' % (ret, guest_trailer) def GetTitle(self): if self.name: return self.name else: return 'Session %i' % (self.seqn, ) def AddDrink(self, drink): super(DrinkingSession, self).AddDrink(drink) session_delta = drink.site.settings.GetSessionTimeoutDelta() defaults = { 'start_time': drink.time, 'end_time': drink.time + session_delta, } # Update or create a SessionChunk. chunk, created = SessionChunk.objects.get_or_create(session=self, user=drink.user, keg=drink.keg, defaults=defaults) chunk.AddDrink(drink) # Update or create a UserSessionChunk. chunk, created = UserSessionChunk.objects.get_or_create( session=self, site=drink.site, user=drink.user, defaults=defaults) chunk.AddDrink(drink) # Update or create a KegSessionChunk. chunk, created = KegSessionChunk.objects.get_or_create( session=self, site=drink.site, keg=drink.keg, defaults=defaults) chunk.AddDrink(drink) def UserChunksByVolume(self): chunks = self.user_chunks.all().order_by('-volume_ml') return chunks def IsActiveNow(self): return self.IsActive(datetime.datetime.now()) def IsActive(self, now): return self.end_time > now def Rebuild(self): self.volume_ml = 0 self.chunks.all().delete() self.user_chunks.all().delete() self.keg_chunks.all().delete() drinks = self.drinks.valid() if not drinks: # TODO(mikey): cancel/delete the session entirely. As it is, session will # remain a placeholder. return session_delta = self.site.settings.GetSessionTimeoutDelta() min_time = None max_time = None for d in drinks: self.AddDrink(d) if min_time is None or d.time < min_time: min_time = d.time if max_time is None or d.time > max_time: max_time = d.time self.start_time = min_time self.end_time = max_time + session_delta self.save() @classmethod def AssignSessionForDrink(cls, drink): # Return existing session if already assigned. if drink.session: return drink.session # Return last session if one already exists q = drink.site.sessions.all().order_by('-end_time')[:1] if q and q[0].IsActive(drink.time): session = q[0] session.AddDrink(drink) drink.session = session drink.save() return session # Create a new session session = cls(start_time=drink.time, end_time=drink.time, site=drink.site) session.save() session.AddDrink(drink) drink.session = session drink.save() return session
class DrinkingSession(_AbstractChunk): """A collection of contiguous drinks. """ class Meta: get_latest_by = 'start_time' ordering = ('-start_time', ) objects = managers.SessionManager() name = models.CharField(max_length=256, blank=True, null=True) def __str__(self): return "Session #%s: %s" % (self.id, self.start_time) def ShortUrl(self): return '%s%s' % (SiteSettings.get().base_url(), reverse('kb-session-short', args=(str(self.id), ))) def HighlightPicture(self): pictures = self.pictures.all().order_by('-time') if pictures: return pictures[0] chunks = self.user_chunks.filter(user__ne=None).order_by('-volume_ml') if chunks: mugshot = chunks[0].user.get_profile().mugshot return mugshot def OtherPictures(self): pictures = self.pictures.all().order_by('-time') if pictures: return pictures[1:] return [] def get_absolute_url(self): dt = timezone.localtime(self.start_time) return reverse('kb-session-detail', args=(), kwargs={ 'year': dt.year, 'month': dt.month, 'day': dt.day, 'pk': self.pk }) def GetStatsRecord(self): qs = SessionStats.objects.filter(session=self).order_by('-id') if len(qs): return qs[0] return None def GetStats(self): ret = {} record = self.GetStatsRecord() if record: ret = record.stats return util.AttrDict(ret) def summarize_drinkers(self): def fmt(user): url = '/drinkers/%s/' % (user.username, ) return '<a href="%s">%s</a>' % (url, user.username) chunks = self.user_chunks.all().order_by('-volume_ml') users = tuple(c.user for c in chunks) names = tuple(fmt(u) for u in users if u) if None in users: guest_trailer = ' (and possibly others)' else: guest_trailer = '' num = len(names) if num == 0: return 'no known drinkers' elif num == 1: ret = names[0] elif num == 2: ret = '%s and %s' % names elif num == 3: ret = '%s, %s and %s' % names else: if guest_trailer: return '%s, %s and at least %i others' % (names[0], names[1], num - 2) else: return '%s, %s and %i others' % (names[0], names[1], num - 2) return '%s%s' % (ret, guest_trailer) def GetTitle(self): if self.name: return self.name else: if self.id: return 'Session %s' % (self.id, ) else: # Not yet saved. return 'New Session' def AddDrink(self, drink): super(DrinkingSession, self).AddDrink(drink) session_delta = SiteSettings.get().GetSessionTimeoutDelta() defaults = { 'start_time': drink.time, 'end_time': drink.time + session_delta, } # Update or create a SessionChunk. chunk, created = SessionChunk.objects.get_or_create(session=self, user=drink.user, keg=drink.keg, defaults=defaults) chunk.AddDrink(drink) # Update or create a UserSessionChunk. chunk, created = UserSessionChunk.objects.get_or_create( session=self, user=drink.user, defaults=defaults) chunk.AddDrink(drink) # Update or create a KegSessionChunk. chunk, created = KegSessionChunk.objects.get_or_create( session=self, keg=drink.keg, defaults=defaults) chunk.AddDrink(drink) def UserChunksByVolume(self): chunks = self.user_chunks.all().order_by('-volume_ml') return chunks def IsActiveNow(self): return self.IsActive(timezone.now()) def IsActive(self, now): return self.end_time > now def Rebuild(self): """Recomputes start and end time, and chunks, based on current drinks. This method should be called after changing the set of drinks belonging to this session. This method has no effect on statistics; see stats module. """ self.volume_ml = 0 self.chunks.all().delete() self.user_chunks.all().delete() self.keg_chunks.all().delete() drinks = self.drinks.all() if not drinks: self.delete() return session_delta = SiteSettings.get().GetSessionTimeoutDelta() min_time = None max_time = None for d in drinks: self.AddDrink(d) if min_time is None or d.time < min_time: min_time = d.time if max_time is None or d.time > max_time: max_time = d.time self.start_time = min_time self.end_time = max_time + session_delta self.save() @classmethod def AssignSessionForDrink(cls, drink): # Return existing session if already assigned. if drink.session: return drink.session # Return last session if one already exists q = DrinkingSession.objects.all().order_by('-end_time')[:1] if q and q[0].IsActive(drink.time): session = q[0] session.AddDrink(drink) drink.session = session drink.save() return session # Create a new session session = cls(start_time=drink.time, end_time=drink.time) session.save() session.AddDrink(drink) drink.session = session drink.save() return session
class DrinkingSession(models.Model): """A collection of contiguous drinks. """ class Meta: get_latest_by = 'start_time' ordering = ('-start_time',) start_time = models.DateTimeField() end_time = models.DateTimeField() volume_ml = models.FloatField(default=0) objects = managers.SessionManager() name = models.CharField(max_length=256, blank=True, null=True) def __unicode__(self): return 'Session #{}: {}'.format(self.id, self.start_time) def Duration(self): return self.end_time - self.start_time def _AddDrinkNoSave(self, drink): session_delta = KegbotSite.get().get_session_timeout_timedelta() session_end = drink.time + session_delta if self.start_time > drink.time: self.start_time = drink.time if self.end_time < session_end: self.end_time = session_end self.volume_ml += drink.volume_ml def AddDrink(self, drink): self._AddDrinkNoSave(drink) self.save() def short_url(self): return '%s%s' % (KegbotSite.get().base_url(), reverse('kb-session-short', args=(str(self.id),))) def full_url(self): return '%s%s' % (KegbotSite.get().base_url(), self.get_absolute_url()) def get_highlighted_picture(self): pictures = self.pictures.all().order_by('-time') if pictures: return pictures[0] return None def get_non_highlighted_pictures(self): pictures = self.pictures.all().order_by('-time') if pictures: return pictures[1:] return [] def get_absolute_url(self): dt = timezone.localtime(self.start_time) return reverse('kb-session-detail', args=(), kwargs={ 'year' : dt.year, 'month' : dt.month, 'day' : dt.day, 'pk' : self.pk}) def get_stats(self): return Stats.get_latest_for_view(session=self) def summarize_drinkers(self): stats = self.get_stats() volmap = stats.get('volume_by_drinker', {}) names = [x for x in reversed(sorted(volmap, key=volmap.get))] if 'guest' in names: guest_trailer = ' (and possibly others)' else: guest_trailer = '' num = len(names) if num == 0: return 'no known drinkers' elif num == 1: ret = names[0] elif num == 2: ret = '{} and {}'.format(*names) elif num == 3: ret = '{}, {} and {}'.format(*names) else: if guest_trailer: return '%s, %s and at least %i others' % (names[0], names[1], num-2) else: return '%s, %s and %i others' % (names[0], names[1], num-2) return '%s%s' % (ret, guest_trailer) def GetTitle(self): if self.name: return self.name else: if self.id: return 'Session %s' % (self.id,) else: # Not yet saved. return 'New Session' def IsActiveNow(self): return self.IsActive(timezone.now()) def IsActive(self, now): return self.end_time > now def Rebuild(self): """Recomputes start time, end time, and volume, based on current drinks. This method should be called after changing the set of drinks belonging to this session. This method has no effect on statistics; see stats module. """ self.volume_ml = 0 drinks = self.drinks.all() if not drinks: self.delete() return session_delta = KegbotSite.get().get_session_timeout_timedelta() min_time = None max_time = None for d in drinks: self.AddDrink(d) if min_time is None or d.time < min_time: min_time = d.time if max_time is None or d.time > max_time: max_time = d.time self.start_time = min_time self.end_time = max_time + session_delta self.save() @classmethod def AssignSessionForDrink(cls, drink): # Return existing session if already assigned. if drink.session: return drink.session # Return last session if one already exists q = DrinkingSession.objects.all().order_by('-end_time')[:1] if q and q[0].IsActive(drink.time): session = q[0] session.AddDrink(drink) drink.session = session drink.save() return session # Create a new session session = cls(start_time=drink.time, end_time=drink.time) session.save() session.AddDrink(drink) drink.session = session drink.save() return session