class Card(TimeStampedBase, OrderedModel): deck = models.ForeignKey(to=Deck, on_delete=models.CASCADE) name = models.CharField( _('name'), max_length=255, ) image = ThumbnailerImageField( upload_to=UploadToAppModelDirectory(), blank=True, resize_source=dict(size=(400, 400)), ) content = MarkdownField(blank=True) categories = models.ManyToManyField(verbose_name=_('categories'), to=Category, blank=True) public = models.BooleanField(default=True) notes = MarkdownField(verbose_name=_("notes"), help_text=_("Staff notes."), blank=True) order_with_respect_to = 'deck' class Meta(OrderedModel.Meta): default_related_name = 'cards' unique_together = ['deck', 'name'] def __str__(self): return self.name
class Demon(AutoOwnedBase, TimeStampedBase): name = models.CharField(verbose_name=_("name"), max_length=255, blank=True) avatar = models.URLField(_('avatar'), max_length=1000, blank=True) tensions = MarkdownField(blank=True) fears = MarkdownField(blank=True) content = MarkdownField(blank=True) class Meta: verbose_name = _('Demon') verbose_name_plural = _('Demons') default_related_name = 'demon' def __str__(self): return self.name or f"{self.owner}'s demon"
class Event(TimeStampedBase): name = models.CharField( verbose_name=_('name'), max_length=255, ) start = models.DateTimeField(verbose_name=_('start'), default=get_current_date) end = models.DateTimeField(verbose_name=_('end'), null=True, blank=True) location = models.CharField(verbose_name=_('location'), max_length=255, blank=True) image_url = models.URLField(verbose_name=_('image url'), max_length=1000, blank=True) video_url = models.URLField(verbose_name=_('video url'), max_length=1000, blank=True) description = models.TextField(verbose_name=_('short description'), blank=True) content = MarkdownField(verbose_name=_('content'), ) category = models.ForeignKey(to=Category, on_delete=models.SET_NULL, blank=True, null=True) participants = models.ManyToManyField( settings.AUTH_USER_MODEL, blank=True, ) class Meta: default_related_name = 'events' ordering = ['-start'] def __str__(self): return self.name
class Routine(OwnedBase, TimeStampedBase, OrderedModel): name = models.CharField( _('name'), max_length=255, ) scope = ScopeField() content = MarkdownField( verbose_name=_('content'), blank=True ) habits = models.ManyToManyField( to=Habit, through='RoutineHabit') # Reverse: owner, reminders # TODO: Related reminders @property def duration(self): return self.habits.aggregate(duration=Sum('duration')).get('duration') order_with_respect_to = 'owner' class Meta(OrderedModel.Meta): default_related_name = 'routines' def __str__(self): return self.name
class Joke(TimeStampedBase): """A motivational quote.""" name = models.CharField(verbose_name=_("name"), max_length=255, unique=True, help_text=_("What is the quote about?")) content = MarkdownField(blank=True) provider = models.ForeignKey(verbose_name=_('provider'), to=settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, blank=True, null=True) accepted = models.BooleanField(verbose_name=_('accepted'), default=False) objects = JokeQuerySet.as_manager() def __str__(self): return self.name @property def pending(self): return not self.accepted def accept(self, save=True): self.accepted = True if save: self.save() class Meta: default_related_name = 'jokes'
class News(TimeStampedBase): name = models.CharField( _('name'), max_length=255, ) author = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, blank=True, null=True) date = models.DateTimeField(_('date'), default=get_current_date) image_url = models.URLField(_('image url'), max_length=1000, blank=True) video_url = models.URLField(_('video url'), max_length=1000, blank=True) description = models.TextField(_('short description'), blank=True) content = MarkdownField() category = models.ForeignKey(to=Category, on_delete=models.SET_NULL, blank=True, null=True) class Meta: default_related_name = 'news' ordering = ['-date'] verbose_name_plural = _('news') def __str__(self): return self.name
class Journal(AutoUrlsMixin, AutoOwnedBase): """ A django model representing a user's journal. """ spellchecker = models.BooleanField(default=False) day_template = MarkdownField( default=render_to_string('journals/dayentries_template.md')) week_template = MarkdownField( default=render_to_string('journals/weekentries_template.md')) month_template = MarkdownField( default=render_to_string('journals/monthentries_template.md')) year_template = MarkdownField( default=render_to_string('journals/yearentries_template.md')) streak = models.PositiveSmallIntegerField( _('streak'), default=0, ) streak_max = models.PositiveSmallIntegerField( _('best streak'), default=0, ) objects = JournalQuerySet.as_manager() class Meta: verbose_name = _('Journal') verbose_name_plural = _('Journals') def __str__(self): return "{}'s journal".format(self.owner) @property def index_url(self): return JournalPage.objects.first().url def reset(self): self.spellchecker = False self.day_template = render_to_string('journals/dayentries_template.md') self.week_template = render_to_string( 'journals/weekentries_template.md') self.month_template = render_to_string( 'journals/monthentries_template.md') self.year_template = render_to_string( 'journals/yearentries_template.md') self.save() return True
class Quote(TimeStampedBase): """A motivational quote.""" name = models.CharField(verbose_name=_("name"), max_length=255, unique=True, help_text=_("What is the quote about?")) content = MarkdownField(blank=True) author = models.CharField(verbose_name=_('author'), max_length=255, blank=True) categories = models.ManyToManyField(verbose_name=_('categories'), to=Category) provider = models.ForeignKey(verbose_name=_('provider'), to=settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, blank=True, null=True) accepted = models.BooleanField(verbose_name=_('accepted'), default=False) used_as_daily = models.DateField(null=True, blank=True, unique=True) liked_by = models.ManyToManyField( to=settings.AUTH_USER_MODEL, related_name='liked_quotes', blank=True, ) disliked_by = models.ManyToManyField( to=settings.AUTH_USER_MODEL, related_name='disliked_quotes', blank=True, ) objects = QuoteQuerySet.as_manager() def __str__(self): return self.name @property def pending(self): return not self.accepted def accept(self, save=True): self.accepted = True if save: self.save() # Notify user if self.provider: self.provider.contact( subject=f'Your quote "{self.name}" has been accepted. EOM') def like(self, user): self.disliked_by.remove(user) self.liked_by.add(user) def dislike(self, user): self.liked_by.remove(user) self.disliked_by.add(user) class Meta: default_related_name = 'quotes'
class Role(TimeStampedBase): kind = models.CharField(max_length=25, choices=KIND_TYPES, default=STANDARD) circle = models.ForeignKey( 'self', null=True, blank=True, on_delete=models.CASCADE, limit_choices_to={'kind': CIRCLE}, ) name = models.CharField( _('name'), max_length=255, ) nickname = models.CharField(_('nickname'), max_length=255, blank=True) item = models.CharField(_('item'), max_length=255, blank=True) icon = ThumbnailerImageField( upload_to='roles/icons/', blank=True, resize_source=dict(size=(100, 100)), ) purpose = MarkdownField(_('purpose'), ) strategy = MarkdownField(_('strategy'), blank=True) domains = MarkdownField(_('domains'), blank=True) accountabilities = MarkdownField(_('accountabilities'), blank=True) policies = MarkdownField(_('policies'), blank=True) history = MarkdownField(_('history'), blank=True) notes = MarkdownField(_('notes'), blank=True) checklists = MarkdownField(_('checklists'), blank=True) metrics = MarkdownField(_('metrics'), blank=True) description = models.TextField(blank=True) objects = RoleQuerySet.as_manager() class Meta: default_related_name = 'roles' ordering = ['name'] unique_together = ['circle', 'name'] def __str__(self): return '{}{}'.format( self.name, ' ({})'.format(self.nickname) if self.nickname else '', ) @property def display_name(self): name = self.nickname or self.name return '{}'.format(name)
class Deck(TimeStampedBase): name = models.CharField( _('name'), max_length=255, ) image = ThumbnailerImageField( upload_to=UploadToAppModelDirectory(), blank=True, resize_source=dict(size=(400, 400)), ) content = MarkdownField(blank=True) notes = MarkdownField(verbose_name=_("notes"), help_text=_("Staff notes."), blank=True) class Meta: default_related_name = 'decks' def __str__(self): return self.name
class Tutorial(TimeStampedBase): name = models.CharField(_('name'), max_length=255, unique=True) video_url = models.URLField(_('video url'), max_length=1000, blank=True) content = MarkdownField() class Meta: default_related_name = 'tutorials' ordering = ['name'] def __str__(self): return self.name
class Adventure(TimeStampedBase): name = models.CharField( _('name'), max_length=255, unique=True ) scope = ScopeField( ) public = models.BooleanField( default=False ) image = ThumbnailerImageField( upload_to=UploadToAppModelDirectory(), blank=True, resize_source=dict(size=(400, 400)), ) image_url = models.URLField( _('image url'), max_length=1000, blank=True ) content = MarkdownField( blank=True ) tags = models.ManyToManyField( to=AdventureTag, blank=True, ) level = models.PositiveSmallIntegerField( _('level'), default=0, ) notes = models.TextField( verbose_name=_("notes"), help_text=_("Staff notes."), blank=True ) adventurers = models.ManyToManyField( settings.AUTH_USER_MODEL, through='AdventureReview' ) @property def rating(self): return self.adventure_reviews.aggregate(Avg('rating')).get('rating__avg') or 0 objects = AdventureQuerySet.as_manager() class Meta: default_related_name = 'adventures' ordering = ['name'] def __str__(self): return self.name
class Tension(OwnedBase, TimeStampedBase): name = models.CharField( verbose_name=_('name'), max_length=255, ) content = MarkdownField(verbose_name=_('content'), blank=True) class Meta: default_related_name = 'tensions' def __str__(self): return self.name
class Circle(TimeStampedBase): name = models.CharField(_('name'), max_length=255, unique=True) purpose = MarkdownField(_('purpose'), ) strategy = MarkdownField(_('strategy'), blank=True) domains = MarkdownField(_('domains'), blank=True) accountabilities = MarkdownField(_('accountabilities'), blank=True) policies = MarkdownField(_('policies'), blank=True) history = MarkdownField(_('history'), blank=True) notes = MarkdownField(_('notes'), blank=True) checklists = MarkdownField(_('checklists'), blank=True) metrics = MarkdownField(_('metrics'), blank=True) # TODO: Adding outcomes class Meta: default_related_name = 'circles' def __str__(self): return self.name
class AdventureReview(OwnedBase, TimeStampedBase): adventure = models.ForeignKey(to=Adventure, on_delete=models.CASCADE) rating = models.PositiveSmallIntegerField( _('rating'), validators=[MinValueValidator(1), MaxValueValidator(5)]) content = MarkdownField(blank=True) image_url = models.URLField(_('image url'), max_length=1000, blank=True) class Meta: default_related_name = 'adventure_reviews' unique_together = ['owner', 'adventure'] def __str__(self): return '{adventure}/{user}'.format(adventure=self.adventure, user=self.owner)
class Quest(OrderedModel): name = models.CharField(verbose_name=_('name'), max_length=255, unique=True) video_url = models.URLField(verbose_name=_('video url'), max_length=1000, blank=True) content = MarkdownField(verbose_name=_('content')) # Reverse: objectives class Meta(OrderedModel.Meta): default_related_name = 'quests' def __str__(self): return self.name
class BookReview(OwnedBase, TimeStampedBase): book = models.ForeignKey( to=Book, on_delete=models.CASCADE ) rating = models.PositiveSmallIntegerField( _('rating'), validators=[MinValueValidator(1), MaxValueValidator(5)] ) area_1 = models.PositiveSmallIntegerField( _('area 1'), validators=[MaxValueValidator(100)] ) area_2 = models.PositiveSmallIntegerField( _('area 2'), validators=[MaxValueValidator(100)] ) area_3 = models.PositiveSmallIntegerField( _('area 3'), validators=[MaxValueValidator(100)] ) area_4 = models.PositiveSmallIntegerField( _('area 4'), validators=[MaxValueValidator(100)] ) area_5 = models.PositiveSmallIntegerField( _('area 5'), validators=[MaxValueValidator(100)] ) area_6 = models.PositiveSmallIntegerField( _('area 6'), validators=[MaxValueValidator(100)] ) area_7 = models.PositiveSmallIntegerField( _('area 7'), validators=[MaxValueValidator(100)] ) content = MarkdownField() class Meta: default_related_name = 'book_reviews' unique_together = ['owner', 'book'] def __str__(self): return 'Book review'
class JournalEntry(OwnedBase, TaggableBase, TimeStampedBase): """ A django model representing a user's journal entry. """ scope = models.CharField( _('scope'), choices=Scope.get_choices(), default=Scope.DAY.value, max_length=5, ) start = models.DateField(_('start'), ) content = MarkdownField(_('content'), blank=True) keywords = models.CharField( max_length=500, help_text="What were the most important experiences/topics?") @property def end(self): return self.get_scope().end def get_scope(self): return get_scope_by_name(self.scope)(self.start) objects = JournalEntryQuerySet.as_manager() class Meta: verbose_name = _('Journal Entry') verbose_name_plural = _('Journal entries') unique_together = ['owner', 'scope', 'start'] ordering = ['-start'] get_latest_by = 'start' default_related_name = 'journal_entries' def __str__(self): return "{} {}".format(self.get_scope_display(), self.start) def save(self, *args, **kwargs): if self.pk is None: # Creation. # Adapting start date to scope. scope = self.get_scope() self.start = scope.start # self.end = scope.end super().save(*args, **kwargs)
class QuestObjective(OrderedModel): quest = models.ForeignKey(to=Quest, on_delete=models.CASCADE) name = models.CharField( _('name'), max_length=255, ) content = MarkdownField(verbose_name=_('content'), blank=True) code = models.CharField( _('code'), max_length=255, ) order_with_respect_to = 'quest' class Meta(OrderedModel.Meta): default_related_name = 'objectives' def __str__(self): return self.name
class Quest(OrderedModel): name = models.CharField( _('name'), max_length=255, ) video_url = models.URLField(_('video url'), max_length=1000, blank=True) content = MarkdownField(verbose_name=_('content')) # Reverse: objectives # questers = models.ManyToManyField( # settings.AUTH_USER_MODEL, # through='UserQuestStatus' # ) class Meta(OrderedModel.Meta): default_related_name = 'quests' def __str__(self): return self.name
class Chapter(TimeStampedBase): story = models.ForeignKey( to=Story, on_delete=models.CASCADE, ) name = models.CharField( _('name'), max_length=255, blank=True ) content = MarkdownField() class Meta: verbose_name = _('Chapter') verbose_name_plural = _('Chapters') default_related_name = 'chapters' ordering = ['-created'] def __str__(self): return "{}".format(self.name)
class Habit(OwnedBase, TimeStampedBase, OrderedModel): name = models.CharField( _('name'), max_length=255, ) scope = ScopeField() icon = models.CharField( _('icon'), max_length=6, blank=True, ) duration = IntuitiveDurationField( _('duration'), default=timezone.timedelta(minutes=10), ) content = MarkdownField(verbose_name=_('content'), blank=True) is_active = models.BooleanField( verbose_name=_('active'), default=True, ) is_controlled = models.BooleanField( verbose_name=_('controlled'), default=False, help_text=_( 'Designates whether this habit is controlled by the system.'), ) streak = models.PositiveSmallIntegerField( _('streak'), default=0, ) streak_max = models.PositiveSmallIntegerField( _('best streak'), default=0, ) @property def is_tracked(self): return self.has_track() def track(self): """ Create a track record for this habit if not already present. And update the streak on successful track creation. :return: `track`: On tracking success """ if not self.has_track(): today = timezone.localtime(timezone.now()).date() # Create the new track record. track, created = self.track_events.get_or_create( created__date=today) if created: self.increase_streak() return track def has_track(self, date=None): """ Check if a track record already exists (for the given date or today). :param date: The date to check for: If a track exists? :return: True if a track event was found. False if not found. """ if date is None: date = timezone.localtime(timezone.now()).date() scope = get_scope_by_name(self.scope)(date) return self.track_events.filter( created__date__range=(scope.start, scope.end)).exists() def reset_streak(self, to=0): """ Reset the streak and update the maximum streak if the new one is higher. :param to: :return: """ updated_fields = ['streak'] if self.streak > self.streak_max: self.streak_max = self.streak updated_fields += ['streak_max'] self.streak = to self.save(update_fields=updated_fields) def increase_streak(self): self.streak += 1 # Update max streak if self.streak > self.streak_max: self.streak_max = self.streak def get_stats(self, phases=4): """ Check the last X phases for completion. Example: Performance of last 4 weeks? Result: [0, 1, 1, 0] => Not yet a success this week, success the 2 weeks before, no success 3 weeks ago. Result type: [{this_phase}, {last_phase}, {pre_last_phase}, {pre_pre_last_phase}] """ # TODO: Refactor to static variables plus updates. (signals?). scope = get_scope_by_name(self.scope)() stats = [0 for x in range(phases)] # [0, 0, 0, ...] empty result array for phase in range(phases): # for each phase start = scope.start end = scope.end stats[phase] = self.track_events.filter( created__date__range=(start, end)).count() scope = scope.previous return stats # Reverse: owner, reminders, track_events order_with_respect_to = 'owner' objects = HabitQuerySet.as_manager() class Meta(OrderedModel.Meta): default_related_name = 'habits' def __str__(self): return self.name
class User(AbstractUser): """ A django model representing a coLegend user/member called 'Legend'. """ # First Name and Last Name do not cover name patterns # around the globe. name = models.CharField( verbose_name=_("name"), max_length=255, help_text=_("Your full name"), ) # personal and contact data MALE = 'M' FEMALE = 'F' NEUTRAL = 'N' GENDER_CHOICES = ( (MALE, _('male')), (FEMALE, _('female')), (NEUTRAL, _('neutral')), ) gender = models.CharField(verbose_name=_('gender'), max_length=1, choices=GENDER_CHOICES, default=NEUTRAL) birthday = models.DateField( verbose_name=_('birthday'), null=True, blank=True, ) address = models.TextField(verbose_name=_('address'), blank=True) @property def city(self): address = self.address city = '' if address: parts = address.splitlines() if len(parts) >= 2: city = parts[1] return city phone = PhoneNumberField( verbose_name=_('phone'), blank=True, help_text=_('International format: e.g "+4917612345678"')) occupation = models.CharField(verbose_name=_('occupation(s)'), max_length=255, blank=True) avatar = ThumbnailerImageField(verbose_name=_('avatar'), upload_to=UploadToOwnedDirectory('avatars'), resize_source=dict(size=(400, 400)), blank=True) title = models.CharField(verbose_name=_('title'), max_length=255, blank=True) purpose = models.CharField( verbose_name=_("legend purpose"), max_length=255, help_text= _("I am a legend, <what you are doing or being> as <the role you are doing it as>." ), default=_( "I am a legend, defining my legend purpose as a member of coLegend." )) notes = MarkdownField(verbose_name=_("notes"), help_text=_("Staff notes."), blank=True) def add_note(self, note): if note: self.notes += '[{timestamp}] {note}\n'.format( timestamp=timezone.localtime(timezone.now()).isoformat(), note=note) self.save() def get_avatar(self, size=ImageSize.MEDIUM.value): # Issue with thumbnail creation: # https://github.com/SmileyChris/easy-thumbnails/issues/499 # https://github.com/SmileyChris/easy-thumbnails/issues/503 try: return self.avatar[size] except InvalidImageFormatError: if self.avatar: return self.avatar return self.avatar or self.get_avatar_fallback(size) def get_avatar_fallback(self, size=ImageSize.MEDIUM.value): email_md5 = hashlib.md5( self.email.encode('utf-8')).hexdigest() if self.email else '' name = quote_plus(self.name) if self.name else 'Anonymous' return 'https://www.gravatar.com/avatar/{email_md5}?s={size}&d=https%3A%2F%2Fui-avatars.com%2Fapi%2F/{name}/{size}/{bg_color}/{fg_color}'.format( email_md5=email_md5, name=name, size=settings.THUMBNAIL_ALIASES.get('')[size]['size'][0], bg_color='A5D6A7', fg_color='fff', ) roles = models.ManyToManyField( Role, blank=True, ) checkpoints = models.ManyToManyField( Checkpoint, through=UserCheckpoint, blank=True, ) balance = models.SmallIntegerField(verbose_name=_('balance'), default=0, help_text=_('¤')) is_premium = models.BooleanField( verbose_name=_('premium'), default=False, help_text=_( 'Designates whether this user has access to premium content.'), ) status = models.TextField( verbose_name=_("status"), blank=True, ) chat_id = models.CharField( verbose_name=_("chat id"), max_length=255, null=True, blank=True, unique=True, ) duo = models.ForeignKey(verbose_name=_('duo'), to=Duo, related_name='members', null=True, blank=True, on_delete=models.SET_NULL) clan = models.ForeignKey(verbose_name=_('clan'), to=Clan, related_name='members', null=True, blank=True, on_delete=models.SET_NULL) tribe = models.ForeignKey(verbose_name=_('tribe'), to=Tribe, related_name='members', null=True, blank=True, on_delete=models.SET_NULL) mentor = models.ForeignKey(verbose_name=_('mentor'), to='self', related_name='mentees', null=True, blank=True, on_delete=models.SET_NULL) def has_checkpoint(self, name): return self.checkpoints.contains_name(name) def add_checkpoint(self, name): # Get the checkpoint. checkpoint, created = Checkpoint.objects.get_or_create(name=name) # Own it if not owned already. UserCheckpoint.objects.get_or_create(owner=self, checkpoint=checkpoint) return checkpoint def has_role(self, name): return self.is_superuser or self.roles.contains_name(name) def add_role(self, name): role, created = Role.objects.get_or_create(name=name) self.roles.add(role) return role @property def legend_days(self): date_joined = self.date_joined now = timezone.now() days = (now - date_joined).days return days @property def steps(self): steps = Step.objects.filter(outcome__owner=self) return steps def __str__(self): return self.username def get_absolute_url(self): return reverse('legends:detail', kwargs={'username': self.username}) objects = UserManager() class Meta: verbose_name = 'legend' verbose_name_plural = 'legends' default_related_name = 'users' ordering = ['username'] registration_country = models.CharField(max_length=255, blank=True) def get_pronoun(self, kind='subject'): gender = self.gender or self.NEUTRAL SUBJECT = 'subject' OBJECT = 'object' POSSESSIVE_ADJECTIVE = 'possessive adjective' POSSESSIVE_PRONOUN = 'possessive pronoun' REFLEXIVE_PRONOUN = 'reflexive pronoun' gender_pronouns = { self.MALE: { SUBJECT: 'he', OBJECT: 'him', POSSESSIVE_ADJECTIVE: 'his', POSSESSIVE_PRONOUN: 'his', REFLEXIVE_PRONOUN: 'himself', }, self.FEMALE: { 'subject': 'she', 'object': 'her', 'possessive adjective': 'her', 'possessive pronoun': 'hers', 'reflexive pronoun': 'herself', }, self.NEUTRAL: { 'subject': 'it', 'object': 'it', 'possessive adjective': 'its', 'possessive pronoun': '', 'reflexive pronoun': 'itself', }, } return gender_pronouns.get(gender).get(kind) def contact(self, sender, subject=None, message=''): if not sender: sender = User.objects.get_or_create(username=SYSTEM_USER_USERNAME) if not subject: subject = 'Message from {name}'.format(name=sender) if self.email: email = EmailMessage(subject=subject, body=message, to=[self.email], reply_to=[sender.email]) email.send()
class Book(TimeStampedBase): name = models.CharField( _('name'), max_length=255, unique=True ) author = models.CharField( _('author'), max_length=255, ) image_url = models.URLField( _('image url'), max_length=1000, blank=True ) url = models.URLField( _('url'), max_length=1000, blank=True ) content = MarkdownField( blank=True ) public = models.BooleanField( default=False ) featured = models.BooleanField( default=False ) tags = models.ManyToManyField( to=BookTag, blank=True, ) notes = models.TextField( verbose_name=_("notes"), help_text=_("Staff notes."), blank=True ) rating = models.FloatField( _('rating'), default=0 ) def calculate_rating(self): rating = self.book_reviews.aggregate(Avg('rating')).get('rating__avg') return round(rating, 2) if rating else 0 def update_rating(self): self.rating = self.calculate_rating() @property def area_ratings(self): return self.book_reviews.aggregate( area_1=Avg('area_1'), area_2=Avg('area_2'), area_3=Avg('area_3'), area_4=Avg('area_4'), area_5=Avg('area_5'), area_6=Avg('area_6'), area_7=Avg('area_7'), ) objects = BookQuerySet.as_manager() class Meta: default_related_name = 'books' ordering = ['name'] def __str__(self): return self.name def save(self, *args, **kwargs): # Making sure only one book can be featured. if self.featured: try: temp = Book.objects.get(featured=True) if self != temp: temp.featured = False temp.save() except Book.DoesNotExist: pass return super().save(*args, **kwargs)
class Habit(OwnedBase, TimeStampedBase, OrderedModel): name = models.CharField( _('name'), max_length=255, ) scope = ScopeField() icon = models.CharField( _('icon'), max_length=6, blank=True, ) duration = IntuitiveDurationField( _('duration'), default=timezone.timedelta(minutes=10), ) content = MarkdownField( verbose_name=_('content'), blank=True ) is_active = models.BooleanField( verbose_name=_('active'), default=True, ) is_controlled = models.BooleanField( verbose_name=_('controlled'), default=False, help_text=_( 'Designates whether this habit is controlled by the system.' ), ) streak = models.PositiveSmallIntegerField( _('streak'), default=0, ) streak_max = models.PositiveSmallIntegerField( _('best streak'), default=0, ) def check_streak(self, date=None): if date is None: date = timezone.localtime(timezone.now()).date() scope = get_scope_by_name(self.scope)(date) else: scope = get_scope_by_name(self.scope)() return self.track_events.filter(created__date__range=(scope.start, scope.end)).exists() def reset_streak(self, to=0): updated_fields = ['streak'] if self.streak > self.streak_max: self.streak_max = self.streak updated_fields += ['streak_max'] self.streak = to self.save(update_fields=updated_fields) def get_stats(self, phases=4): """ Check the last X phases for completion. Example: Performance of last 4 weeks? Result: [0, 1, 1, 0] => Not yet a success this week, success the 2 weeks before, no success 3 weeks ago. Result type: [{this_phase}, {last_phase}, {pre_last_phase}, {pre_pre_last_phase}] """ # TODO: Refactor to static variables plus updates. (signals?). scope = get_scope_by_name(self.scope)() stats = [0 for x in range(phases)] # [0, 0, 0, ...] empty result array for phase in range(phases): # for each phase start = scope.start end = scope.end stats[phase] = self.track_events.filter(created__date__range=(start, end)).count() scope = scope.previous return stats # Reverse: owner, reminders, track_events order_with_respect_to = 'owner' objects = HabitQuerySet.as_manager() class Meta(OrderedModel.Meta): default_related_name = 'habits' def __str__(self): return self.name
class Lead(TimeStampedBase): name = models.CharField( verbose_name=_('name'), max_length=255, ) status = models.ForeignKey( to=Status, on_delete=models.PROTECT, default=get_default_status ) email = models.EmailField( _('email address'), blank=True, null=True, unique=True, ) phone = PhoneNumberField( verbose_name=_('phone'), blank=True, help_text=_('International format: e.g "+4917612345678"') ) url = models.URLField( _('url'), max_length=1000, blank=True, null=True, unique=True, ) country = CountryField( verbose_name=_('country'), blank=True ) address = models.TextField( verbose_name=_('address'), blank=True ) gender = models.CharField( verbose_name=_('gender'), max_length=1, choices=User.GENDER_CHOICES, default=User.NEUTRAL ) birthday = models.DateField( verbose_name=_('birthday'), null=True, blank=True, ) history = MarkdownField( verbose_name=_('history'), blank=True ) notes = MarkdownField( verbose_name=_('notes'), blank=True ) tags = models.ManyToManyField( to=Tag, blank=True ) first_contact = models.DateField( _('first contact'), blank=True, null=True, help_text=_('When was this lead first contacted?'), ) last_contact = models.DateField( _('last contact'), blank=True, null=True, help_text=_('When was this lead last contacted?'), ) next_contact = models.DateField( _('next contact'), blank=True, null=True, help_text=_('When will this lead be contacted next?'), ) creator = models.ForeignKey( to=settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL ) @property def contact(self): return self.email or self.url or self.phone class Meta: default_related_name = 'leads' verbose_name = _("Lead") verbose_name_plural = _("Leads") ordering = ['created'] def __str__(self): return self.name
class Vision(AutoUrlsMixin, OwnedBase, TimeStampedBase): """ A django model representing a user's vision. """ DAY = 1 WEEK = 2 MONTH = 3 QUARTER = 4 YEAR = 5 SOMEDAY = 6 SCOPE_CHOICES = ( (DAY, _('day')), (WEEK, _('week')), (MONTH, _('month')), (QUARTER, _('quarter')), (YEAR, _('year')), (SOMEDAY, _('someday')), ) SCOPE_MAP = OrderedDict(SCOPE_CHOICES) scope = models.PositiveSmallIntegerField( _('scope'), choices=SCOPE_CHOICES, default=DAY, ) image = ThumbnailerImageField(verbose_name=_('image'), upload_to=UploadToOwnedDirectory('vision'), resize_source=dict(size=(1200, 1200)), blank=True) content = MarkdownField(blank=True) objects = VisionQuerySet.as_manager() class Meta: verbose_name = _('vision') verbose_name_plural = _('visions') default_related_name = 'visions' unique_together = ('owner', 'scope') def __str__(self): return "{}'s {} vision".format(self.owner, self.get_scope_display()) @property def create_url(self): url = '{}create'.format(self.auto_url_prefix) return reverse(url, kwargs={'scope': self.get_scope_display()}) @property def detail_url(self): url = '{}detail'.format(self.auto_url_prefix) return reverse(url, kwargs={'scope': self.get_scope_display()}) @property def update_url(self): url = '{}update'.format(self.auto_url_prefix) return reverse(url, kwargs={'scope': self.get_scope_display()}) @property def delete_url(self): url = '{}delete'.format(self.auto_url_prefix) return reverse(url, kwargs={'scope': self.get_scope_display()})
class Hero(AutoOwnedBase, TimeStampedBase): name = models.CharField(verbose_name=_("name"), max_length=255, blank=True) avatar = models.URLField(_('avatar'), max_length=1000, blank=True) year_topic = models.CharField(verbose_name=_("year topic"), max_length=255, blank=True) vision = MarkdownField(blank=True) mission = MarkdownField(blank=True) values = MarkdownField(blank=True) powers = MarkdownField(blank=True) skills = MarkdownField(blank=True) habits = MarkdownField(blank=True) principles = MarkdownField(blank=True) wishes = MarkdownField(blank=True) goals = MarkdownField(blank=True) people = MarkdownField(blank=True) resources = MarkdownField(blank=True) achievements = MarkdownField(blank=True) questions = MarkdownField(blank=True) experiments = MarkdownField(blank=True) projects = MarkdownField(blank=True) bucket = MarkdownField(blank=True, help_text="bucket list") inspirations = MarkdownField(blank=True) roles = MarkdownField(blank=True) strategy = MarkdownField(blank=True) topics = MarkdownField(blank=True) routines = MarkdownField(blank=True) blueprint_day = MarkdownField(blank=True) blueprint_week = MarkdownField(blank=True) blueprint_month = MarkdownField(blank=True) content = MarkdownField(blank=True) class Meta: verbose_name = _('Hero') verbose_name_plural = _('Heroes') default_related_name = 'hero' def __str__(self): return self.name or f"{self.owner}'s hero"