class Memo(creme_models.CremeModel): content = models.TextField(_('Content')) on_homepage = models.BooleanField(_('Displayed on homepage'), blank=True, default=False) creation_date = creme_fields.CreationDateTimeField(_('Creation date'), editable=False) user = creme_fields.CremeUserForeignKey(verbose_name=_('Owner user')) entity_content_type = creme_fields.EntityCTypeForeignKey(related_name='+', editable=False) entity = models.ForeignKey( creme_models.CremeEntity, related_name='assistants_memos', editable=False, on_delete=models.CASCADE, ).set_tags(viewable=False) creme_entity = creme_fields.RealEntityForeignKey( ct_field='entity_content_type', fk_field='entity', ) objects = MemoManager() creation_label = _('Create a memo') save_label = _('Save the memo') class Meta: app_label = 'assistants' verbose_name = _('Memo') verbose_name_plural = _('Memos') def __str__(self): # NB: translate for unicode can not take 2 arguments... return ellipsis(self.content.strip().replace('\n', ''), 25) def get_edit_absolute_url(self): return reverse('assistants__edit_memo', args=(self.id,)) def get_related_entity(self): # For generic views return self.creme_entity
class CommercialApproach(creme_models.CremeModel): title = models.CharField(_('Title'), max_length=200) # ok_or_in_futur = models.BooleanField(_('Done?'), editable=False, default=False) description = models.TextField(_('Description'), blank=True) creation_date = creme_fields.CreationDateTimeField(_('Creation date'), editable=False) related_activity = models.ForeignKey( settings.ACTIVITIES_ACTIVITY_MODEL, null=True, editable=False, on_delete=models.CASCADE, ) # entity_content_type = models.ForeignKey(ContentType, related_name="comapp_entity_set", editable=False, on_delete=models.CASCADE) # entity_id = models.PositiveIntegerField(editable=False) # .set_tags(viewable=False) uncomment if it becomes an auxiliary (get_related_entity()) # creme_entity = GenericForeignKey(ct_field="entity_content_type", fk_field="entity_id") entity_content_type = creme_fields.EntityCTypeForeignKey(related_name='+', editable=False) entity = models.ForeignKey( creme_models.CremeEntity, related_name='commercial_approaches', editable=False, on_delete=models.CASCADE, ) # .set_tags(viewable=False) uncomment if it becomes an auxiliary (get_related_entity()) creme_entity = creme_fields.RealEntityForeignKey( ct_field='entity_content_type', fk_field='entity') creation_label = _('Create a commercial approach') save_label = _('Save the commercial approach') class Meta: app_label = 'commercial' verbose_name = _('Commercial approach') verbose_name_plural = _('Commercial approaches') def __str__(self): return self.title @staticmethod def get_approaches(entity_pk=None): # queryset = CommercialApproach.objects.filter(ok_or_in_futur=False) \ # .select_related('related_activity') queryset = CommercialApproach.objects.select_related( 'related_activity') return queryset.filter(entity_id=entity_pk) if entity_pk else \ queryset.exclude(entity__is_deleted=True) @staticmethod def get_approaches_for_ctypes(ct_ids): warnings.warn( 'CommercialApproach.get_approaches_for_ctypes() is deprecated.', DeprecationWarning) # return CommercialApproach.objects.filter(entity_content_type__in=ct_ids, ok_or_in_futur=False) \ return CommercialApproach.objects.filter(entity_content_type__in=ct_ids) \ .select_related('related_activity')
class Alert(creme_models.CremeModel): title = models.CharField(_('Title'), max_length=200) description = models.TextField(_('Description'), blank=True) is_validated = models.BooleanField(_('Validated'), editable=False, default=False) reminded = models.BooleanField(_('Notification sent'), editable=False, default=False) # Need by creme_core.core.reminder trigger_date = models.DateTimeField(_('Trigger date')) user = creme_fields.CremeUserForeignKey(verbose_name=_('Owner user')) entity_content_type = creme_fields.EntityCTypeForeignKey(related_name='+', editable=False) entity = models.ForeignKey(creme_models.CremeEntity, related_name='assistants_alerts', editable=False, on_delete=models.CASCADE, ).set_tags(viewable=False) creme_entity = creme_fields.RealEntityForeignKey(ct_field='entity_content_type', fk_field='entity') objects = AlertManager() creation_label = _('Create an alert') save_label = _('Save the alert') class Meta: app_label = 'assistants' verbose_name = _('Alert') verbose_name_plural = _('Alerts') def __str__(self): return self.title def get_edit_absolute_url(self): return reverse('assistants__edit_alert', args=(self.id,)) # @staticmethod # def get_alerts(entity): # warnings.warn('Alert.get_alerts() is deprecated.', DeprecationWarning) # return Alert.objects.filter(is_validated=False, entity_id=entity.id).select_related('user') # @staticmethod # def get_alerts_for_home(user): # warnings.warn('Alert.get_alerts_for_home() is deprecated.', DeprecationWarning) # return Alert.objects.filter(is_validated=False, # user__in=[user] + user.teams, # )\ # .select_related('user') # @staticmethod # def get_alerts_for_ctypes(ct_ids, user): # warnings.warn('Alert.get_alerts_for_ctypes() is deprecated.', DeprecationWarning) # return Alert.objects.filter(entity_content_type__in=ct_ids, user__in=[user] + user.teams, is_validated=False) \ # .select_related('user') def get_related_entity(self): # For generic views return self.creme_entity @property def to_be_reminded(self): return not self.is_validated and not self.reminded
class Action(creme_models.CremeModel): title = models.CharField(_('Title'), max_length=200) is_ok = models.BooleanField( _('Expected reaction has been done'), editable=False, default=False, ) description = models.TextField(_('Source action'), blank=True) creation_date = creme_fields.CreationDateTimeField(_('Creation date'), editable=False) expected_reaction = models.TextField(_('Target action'), blank=True) deadline = models.DateTimeField(_('Deadline')) validation_date = models.DateTimeField( _('Validation date'), blank=True, null=True, editable=False, ) user = creme_fields.CremeUserForeignKey(verbose_name=_('Owner user')) entity_content_type = creme_fields.EntityCTypeForeignKey(related_name='+', editable=False) entity = models.ForeignKey( creme_models.CremeEntity, related_name='assistants_actions', editable=False, on_delete=models.CASCADE, ).set_tags(viewable=False) creme_entity = creme_fields.RealEntityForeignKey( ct_field='entity_content_type', fk_field='entity', ) objects = ActionManager() creation_label = _('Create an action') save_label = _('Save the action') class Meta: app_label = 'assistants' verbose_name = _('Action') verbose_name_plural = _('Actions') def __str__(self): return self.title def get_edit_absolute_url(self): return reverse('assistants__edit_action', args=(self.id, )) def get_related_entity(self): # For generic views return self.creme_entity
class Memo(creme_models.CremeModel): content = models.TextField(_('Content')) on_homepage = models.BooleanField(_('Displayed on homepage'), blank=True, default=False) creation_date = creme_fields.CreationDateTimeField(_('Creation date'), editable=False) user = creme_fields.CremeUserForeignKey(verbose_name=_('Owner user')) entity_content_type = creme_fields.EntityCTypeForeignKey(related_name='+', editable=False) entity = models.ForeignKey(creme_models.CremeEntity, related_name='assistants_memos', editable=False, on_delete=models.CASCADE, ).set_tags(viewable=False) creme_entity = creme_fields.RealEntityForeignKey(ct_field='entity_content_type', fk_field='entity') objects = MemoManager() creation_label = _('Create a memo') save_label = _('Save the memo') class Meta: app_label = 'assistants' verbose_name = _('Memo') verbose_name_plural = _('Memos') def __str__(self): # NB: translate for unicode can not take 2 arguments... return ellipsis(self.content.strip().replace('\n', ''), 25) def get_edit_absolute_url(self): return reverse('assistants__edit_memo', args=(self.id,)) # @staticmethod # def get_memos(entity): # warnings.warn('Memo.get_memos() is deprecated.', DeprecationWarning) # return Memo.objects.filter(entity_id=entity.id).select_related('user') # @staticmethod # def get_memos_for_home(user): # warnings.warn('Memo.get_memos_for_home() is deprecated.', DeprecationWarning) # return Memo.objects.filter(on_homepage=True, # user__in=[user] + user.teams, # ) \ # .select_related('user') # @staticmethod # def get_memos_for_ctypes(ct_ids, user): # warnings.warn('Memo.get_memos_for_ctypes() is deprecated.', DeprecationWarning) # return Memo.objects.filter(entity_content_type__in=ct_ids, user__in=[user] + user.teams) \ # .select_related('user') def get_related_entity(self): # For generic views return self.creme_entity
class ToDo(CremeModel): title = models.CharField(_('Title'), max_length=200) is_ok = models.BooleanField(_('Done?'), editable=False, default=False) # Needed by creme_core.core.reminder reminded = models.BooleanField(_('Notification sent'), editable=False, default=False) description = models.TextField(_('Description'), blank=True) creation_date = creme_fields.CreationDateTimeField(_('Creation date'), editable=False) deadline = models.DateTimeField(_('Deadline'), blank=True, null=True) user = creme_fields.CremeUserForeignKey(verbose_name=_('Owner user')) entity_content_type = creme_fields.EntityCTypeForeignKey(related_name='+', editable=False) entity = models.ForeignKey( CremeEntity, related_name='assistants_todos', editable=False, on_delete=models.CASCADE, ).set_tags(viewable=False) creme_entity = creme_fields.RealEntityForeignKey( ct_field='entity_content_type', fk_field='entity', ) objects = ToDoManager() creation_label = _('Create a todo') save_label = _('Save the todo') class Meta: app_label = 'assistants' verbose_name = _('Todo') verbose_name_plural = _('Todos') def __str__(self): return self.title def get_edit_absolute_url(self): return reverse('assistants__edit_todo', args=(self.id,)) def get_related_entity(self): # For generic views return self.creme_entity @property def to_be_reminded(self): return self.deadline and not self.is_ok and not self.reminded
class CommercialApproach(creme_models.CremeModel): title = models.CharField(_('Title'), max_length=200) description = models.TextField(_('Description'), blank=True) creation_date = creme_fields.CreationDateTimeField(_('Creation date'), editable=False) related_activity = models.ForeignKey( settings.ACTIVITIES_ACTIVITY_MODEL, null=True, editable=False, on_delete=models.CASCADE, ) entity_content_type = creme_fields.EntityCTypeForeignKey(related_name='+', editable=False) entity = models.ForeignKey( creme_models.CremeEntity, related_name='commercial_approaches', editable=False, on_delete=models.CASCADE, ) # .set_tags(viewable=False) uncomment if it becomes an auxiliary (get_related_entity()) creme_entity = creme_fields.RealEntityForeignKey( ct_field='entity_content_type', fk_field='entity') creation_label = _('Create a commercial approach') save_label = _('Save the commercial approach') class Meta: app_label = 'commercial' verbose_name = _('Commercial approach') verbose_name_plural = _('Commercial approaches') def __str__(self): return self.title @staticmethod def get_approaches(entity_pk=None): queryset = CommercialApproach.objects.select_related( 'related_activity') return queryset.filter(entity_id=entity_pk) if entity_pk else \ queryset.exclude(entity__is_deleted=True)
class UserMessage(CremeModel): title = models.CharField(_('Title'), max_length=200) body = models.TextField(_('Message body')) creation_date = models.DateTimeField(_('Creation date')) priority = models.ForeignKey( UserMessagePriority, verbose_name=_('Priority'), on_delete=models.PROTECT, ) sender = creme_fields.CremeUserForeignKey( verbose_name=_('Sender'), related_name='sent_assistants_messages_set', ) recipient = creme_fields.CremeUserForeignKey( verbose_name=_('Recipient'), related_name='received_assistants_messages_set', ) email_sent = models.BooleanField(default=False) entity_content_type = creme_fields.EntityCTypeForeignKey( null=True, related_name='+', editable=False, ) entity = models.ForeignKey( CremeEntity, null=True, related_name='assistants_messages', editable=False, on_delete=models.CASCADE, ).set_tags(viewable=False) creme_entity = creme_fields.RealEntityForeignKey( ct_field='entity_content_type', fk_field='entity', ) creation_label = _('Create a message') save_label = _('Save the message') class Meta: app_label = 'assistants' verbose_name = _('User message') verbose_name_plural = _('User messages') def __str__(self): return self.title @classmethod @atomic def create_messages(cls, users, title, body, priority_id, sender, entity): """Create UserMessages instances to sent to several users. Notice that teams are treated as several Users. @param users: A sequence of CremeUser objects ; duplicates are removed. """ users_map = {} for user in users: if user.is_team: users_map.update(user.teammates) else: users_map[user.id] = user build_msg = partial( cls, creation_date=now(), title=title, body=body, priority_id=priority_id, sender=sender, creme_entity=entity, ) cls.objects.bulk_create( build_msg(recipient=user) for user in users_map.values()) from ..creme_jobs import usermessages_send_type usermessages_send_type.refresh_job() @classmethod def send_mails(cls, job): from django.conf import settings from django.core.mail import EmailMessage, get_connection usermessages = [*cls.objects.filter(email_sent=False)] if not usermessages: return subject_format = gettext('User message from Creme: {}') body_format = gettext('{user} sent you the following message:\n{body}') EMAIL_SENDER = settings.EMAIL_SENDER messages = [ EmailMessage( subject_format.format(msg.title), body_format.format(user=msg.sender, body=msg.body), EMAIL_SENDER, [msg.recipient.email], ) for msg in usermessages if msg.recipient.email ] try: with get_connection() as connection: connection.send_messages(messages) except Exception as e: logger.critical('Error while sending user-messages emails (%s)', e) JobResult.objects.create( job=job, messages=[ gettext('An error occurred while sending emails'), gettext('Original error: {}').format(e), ], ) cls.objects.filter(pk__in=[m.id for m in usermessages]) \ .update(email_sent=True)
class ToDo(CremeModel): title = models.CharField(_('Title'), max_length=200) is_ok = models.BooleanField(_('Done ?'), editable=False, default=False) reminded = models.BooleanField( _('Notification sent'), editable=False, default=False) # Needed by creme_core.core.reminder description = models.TextField(_('Description'), blank=True) creation_date = creme_fields.CreationDateTimeField(_('Creation date'), editable=False) deadline = models.DateTimeField(_('Deadline'), blank=True, null=True) user = creme_fields.CremeUserForeignKey(verbose_name=_('Owner user')) entity_content_type = creme_fields.EntityCTypeForeignKey(related_name='+', editable=False) entity = models.ForeignKey( CremeEntity, related_name='assistants_todos', editable=False, on_delete=models.CASCADE, ).set_tags(viewable=False) creme_entity = creme_fields.RealEntityForeignKey( ct_field='entity_content_type', fk_field='entity') objects = ToDoManager() creation_label = _('Create a todo') save_label = _('Save the todo') class Meta: app_label = 'assistants' verbose_name = _('Todo') verbose_name_plural = _('Todos') def __str__(self): return self.title def get_edit_absolute_url(self): return reverse('assistants__edit_todo', args=(self.id, )) # @staticmethod # def get_todos(entity): # warnings.warn('ToDo.get_todos() is deprecated.', DeprecationWarning) # return ToDo.objects.filter(entity_id=entity.id).select_related('user') # @staticmethod # def get_todos_for_home(user): # warnings.warn('ToDo.get_todos_for_home() is deprecated ; ' # 'use ToDo.objects.filter_by_user() instead.', # DeprecationWarning # ) # return ToDo.objects.filter(user__in=[user] + user.teams)\ # .select_related('user') # @staticmethod # def get_todos_for_ctypes(ct_ids, user): # warnings.warn('ToDo.get_todos_for_ctypes() is deprecated.', DeprecationWarning) # return ToDo.objects.filter(entity_content_type__in=ct_ids, # user__in=[user] + user.teams # ).select_related('user') def get_related_entity(self): # For generic views return self.creme_entity @property def to_be_reminded(self): return self.deadline and not self.is_ok and not self.reminded
class AbstractAddress(CremeModel): name = models.CharField(_('Name'), max_length=100, blank=True) address = models.TextField(_('Address'), blank=True) po_box = models.CharField(_('PO box'), max_length=50, blank=True).set_tags(optional=True) zipcode = models.CharField(_('Zip code'), max_length=100, blank=True).set_tags(optional=True) city = models.CharField(_('City'), max_length=100, blank=True).set_tags(optional=True) department = models.CharField(_('Department'), max_length=100, blank=True).set_tags(optional=True) state = models.CharField(_('State'), max_length=100, blank=True).set_tags(optional=True) country = models.CharField(_('Country'), max_length=40, blank=True).set_tags(optional=True) content_type = creme_fields.EntityCTypeForeignKey(related_name='+', editable=False) \ .set_tags(viewable=False) object = models.ForeignKey(CremeEntity, related_name='persons_addresses', editable=False, on_delete=models.CASCADE, ).set_tags(viewable=False) owner = creme_fields.RealEntityForeignKey(ct_field='content_type', fk_field='object') creation_label = _('Create an address') save_label = _('Save the address') STR_FIELD_NAMES = [ ['address', 'zipcode', 'city', 'department'], ['po_box', 'state', 'country'], ] STR_SEPARATOR = ' ' # class Meta: class Meta(CremeModel.Meta): abstract = True app_label = 'persons' verbose_name = _('Address') verbose_name_plural = _('Addresses') ordering = ('id',) def __str__(self): s = '' join = self.STR_SEPARATOR.join allowed_fnames = {*self.info_field_names()} get_field_value = (lambda fname: None if fname not in allowed_fnames else getattr(self, fname)) for field_names in self.STR_FIELD_NAMES: s = join(filter(None, (get_field_value(fn) for fn in field_names))) if s: break return s def get_edit_absolute_url(self): return reverse('persons__edit_address', args=(self.id,)) def get_related_entity(self): # For generic views return self.owner def __bool__(self): # Used by forms to detect empty addresses return any(fvalue for fname, fvalue in self.info_fields) def clone(self, entity): """Returns a new cloned (saved) address for a (saved) entity.""" # return Address.objects.create(owner=entity, **dict(self.info_fields)) return type(self).objects.create(owner=entity, **dict(self.info_fields)) @classmethod def info_field_names(cls): is_field_hidden = FieldsConfig.get_4_model(cls).is_field_hidden excluded = {'id', 'content_type', 'object'} # TODO: just exclude not viewable ? return tuple(f.name for f in cls._meta.fields if f.name not in excluded and not is_field_hidden(f) ) @property def info_fields(self): for fname in self.info_field_names(): yield fname, getattr(self, fname)