class Survey(models.Model): title = models.CharField(_('survey title'), max_length=255) slug = models.SlugField(_('slug'), max_length=255, unique=True) description = models.TextField( verbose_name=_("description"), help_text= _("This field appears on the public web site and should give an overview to the interviewee" ), blank=True) ## Add validation on datetimes opens = models.DateTimeField(_('survey starts accepting submissions on')) closes = models.DateTimeField(_('survey stops accepting submissions on')) # Define the behavior of the survey visible = models.BooleanField(_('survey is visible')) public = models.BooleanField(_('survey results are public')) restricted = models.BooleanField( verbose_name=_("restrict the survey to authentified user"), blank=True, default=False) allows_multiple_interviews = models.BooleanField( verbose_name=_("allows multiple interviews"), blank=True, default=True) # template_name = models.CharField(_('template name'),max_length=150, # null=True, blank=True, # help_text=_("This field is used to define a custom template " # "(Example: 'dj_survey/template/my_add_interview_forms.html').")) # Control who can edit the survey # TODO: Plug this control in the view used to edit the survey created_by = models.ForeignKey(User, related_name="created_surveys") editable_by = models.ForeignKey(User, related_name="owned_surveys") # Integration in Pinax recipient_type = models.ForeignKey(ContentType, blank=True, null=True) recipient_id = models.PositiveIntegerField(blank=True, null=True) recipient = generic.GenericForeignKey('recipient_type', 'recipient_id') objects = SurveyManager() @property def _cache_name(self): if not self.id: id = 'new' else: id = int(self.id) return 'survey_' + repr(id) + '_status' @property def open(self): if not self.visible: return False value = cache.get(self._cache_name) if value is not None: return value now = datetime.datetime.now() if self.opens >= now: value = False duration = (now - self.opens).seconds elif self.closes >= now: value = True duration = (self.opens - now).seconds else: value = False duration = 60 * 60 * 24 * 31 if duration: cache.set(self._cache_name, value, duration) return value @property def closed(self): return not self.open @property def status(self): if not self.visible: return _('private') if self.open: return _('open') if datetime.now() < self.opens: return unicode(_('opens ')) + datefilter(self.opens) return _('closed') @property def answer_count(self): if hasattr(self, '_answer_count'): return self._answer_count self._answer_count = sum(q.answer_count for q in self.questions.iterator()) return self._answer_count @property def interview_count(self): # NOTSURE: Do we realy need this optimisation? if hasattr(self, '_interview_count'): return self._interview_count self._interview_count = len( Answer.objects.filter( question__survey=self.id).values('interview_uuid').distinct()) return self._interview_count @property def session_key_count(self): # NOTSURE: Do we realy need this optimisation? if hasattr(self, '_session_key_count'): return self._submission_count self._submission_count = len( Answer.objects.filter( question__survey=self.id).values('session_key').distinct()) return self._submission_count def has_answers_from(self, session_key): return bool( Answer.objects.filter( session_key__exact=session_key.lower(), question__survey__id__exact=self.id).distinct().count()) def __unicode__(self): return u' - '.join([self.slug, self.title]) @models.permalink def get_absolute_url(self): return ('survey-detail', (), {'survey_slug': self.slug}) def save(self): res = super(Survey, self).save() cache.delete(self._cache_name) return res def answers_viewable_by(self, user): if not self.visible: return False if self.public: return True if user.is_anonymous(): return False return user.has_perm('survey.view_answers')
class Model(models.Model): field = generic.GenericForeignKey()
class Message(models.Model): """ A Message. We have a uuid, which is our reference. We also have a gateway_message_id, which is their reference. This is required by some systems so we can pass in a unique value that will allow us to match up replies to original messages. """ content = models.TextField(help_text=_('The body of the message.')) recipient_number = models.CharField( max_length=32, help_text=_( 'The international number of the recipient, without the leading +') ) sender = models.ForeignKey('auth.User', related_name='sent_sms_messages') sender_number = models.CharField( max_length=32, null=True, blank=True, help_text=_( 'The international number of the sender, without the leading +')) send_date = models.DateTimeField(null=True, blank=True, editable=False) delivery_date = models.DateTimeField(null=True, blank=True, editable=False) uuid = uuidfield.fields.UUIDField( auto=True, help_text=_('Used for associating replies.')) status = models.CharField(max_length=16, choices=MESSAGE_STATUSES, default="Unsent") status_message = models.CharField(max_length=128, null=True, blank=True) billed = models.BooleanField(default=False) content_type = models.ForeignKey('contenttypes.ContentType') object_id = models.PositiveIntegerField() billee = generic.GenericForeignKey() gateway = models.ForeignKey('sms.Gateway', null=True, blank=True) gateway_message_id = models.CharField(max_length=128, blank=True, null=True, editable=False) #reply_callback = picklefield.PickledObjectField(null=True, blank=True) reply_callback = models.CharField(max_length=128, null=True, blank=True) gateway_charge = models.DecimalField(max_digits=10, decimal_places=5, null=True, blank=True) objects = MessageManager() class Meta: app_label = 'sms' permissions = (('view_message', 'Can view message'), ) ordering = ('send_date', ) def send(self, gateway): gateway.send(self) @property def length(self): """Unicode messages are limited to 70 chars/message segment.""" # try: # return len(str(self.content)) / 160 + 1 # except UnicodeEncodeError: # return len(self.content) / 70 + 1 return len(self.content) / 160 + 1 @property def local_send_time(self): if self.billee.timezone: return adjust_datetime_to_timezone(self.send_date, settings.TIME_ZONE, self.billee.timezone) return self.send_date def __unicode__(self): return "[%s] Sent to %s by %s at %s [%i]" % ( self.status, self.recipient_number, self.sender, self.send_date, self.length)
class BlogEntry(SlugifiedModelMixin, ImagableItemMixin): """ Basic class for simplified blog entries. Blog entry may be tied to another content, for example organization, project or location. This way we can use custom managers to filter content from such modules. """ content = models.TextField(default="", verbose_name=_(u"content")) date_created = models.DateTimeField(auto_now_add=True, verbose_name=_(u"date created")) date_edited = models.DateTimeField(auto_now=True, verbose_name=_(u"date edited")) author = models.ForeignKey(User, related_name="simpleblog_entries", verbose_name=_(u"author")) category = models.ForeignKey(BlogCategory, blank=True, null=True, verbose_name=_(u"category")) tags = models.CharField(default="", blank=True, max_length=255, verbose_name=_(u"tags"), help_text=_(u"List of tags separated by comma")) # By defining generic relation we can decide where to publish this entry content_type = models.ForeignKey(ContentType, blank=True, null=True) object_id = models.PositiveIntegerField(blank=True, null=True) content_object = generic.GenericForeignKey('content_type', 'object_id') objects = BlogManager() def get_absolute_url(self): if self.content_object is not None: model_name = self.content_object._meta.model_name if model_name == 'organization': return reverse('organizations:news-detail', kwargs={ 'slug': self.content_object.slug, 'news_slug': self.slug, }) elif model_name == 'idea': return reverse('locations:idea-news-list', kwargs={ 'location_slug': self.content_object.location.slug, 'slug': self.content_object.slug, }) elif model_name == 'socialproject': return reverse('projects:news-detail', kwargs={ 'slug': self.content_object.slug, 'news_pk': self.pk, }) return reverse('simpleblog:detail', kwargs={'slug': self.slug}) def save(self, *args, **kwargs): self.content = sanitizeHtml(self.content) super(BlogEntry, self).save(*args, **kwargs) def has_access(self, user): """ Check if particular user can delete/update this entry. """ if user.is_superuser: return True return user == self.author def __str__(self): return self.name class Meta: ordering = [ '-date_created', ] verbose_name = _(u"blog entry") verbose_name_plural = _(u"blog entries")
class CustomFieldValue(TimeStampMixin, models.Model): custom_field = models.ForeignKey(CustomField, verbose_name=_('key'), on_delete=models.PROTECT) # value is stored in charfield on purpose - ralph's custom field mechanism # is by-design simple, so it, for example, doesn't allow to filter by range # of integers or other Django filters like gte, lte. value = models.CharField(max_length=CUSTOM_FIELD_VALUE_MAX_LENGTH) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField(db_index=True) object = generic.GenericForeignKey('content_type', 'object_id') objects = models.Manager() # generic relation has to use specific manager (queryset) # which handle inheritance inherited_objects = CustomFieldValueQuerySet.as_manager() class Meta: unique_together = ('custom_field', 'content_type', 'object_id') def __str__(self): return '{} ({}): {}'.format( self.custom_field if self.custom_field_id else None, self.object, self.value) def _get_unique_checks(self, exclude=None): if exclude: for k in ['content_type', 'object_id']: try: exclude.remove(k) except ValueError: pass return super()._get_unique_checks() def unique_error_message(self, model_class, unique_check): """ Return better unique validation message than standard Django message. """ opts = model_class._meta params = { 'model': self, 'model_class': model_class, 'model_name': six.text_type(capfirst(opts.verbose_name)), 'unique_check': unique_check, } if len(unique_check) > 1: return ValidationError( message= _("Custom field of the same type already exists for this object." # noqa ), code='unique_together', params=params, ) return super().unique_error_message(self, model_class, unique_check) def clean(self): if self.custom_field_id: self.custom_field.get_form_field().clean(self.value) super().clean()
class Document(ResourceBase): """ A document is any kind of information that can be attached to a map such as pdf, images, videos, xls... """ # Relation to the resource model content_type = models.ForeignKey(ContentType, blank=True, null=True) object_id = models.PositiveIntegerField(blank=True, null=True) resource = generic.GenericForeignKey('content_type', 'object_id') doc_file = models.FileField(upload_to='documents', null=True, blank=True, verbose_name=_('File')) extension = models.CharField(max_length=128, blank=True, null=True) doc_url = models.URLField( blank=True, null=True, help_text=_('The URL of the document if it is external.'), verbose_name=_('URL')) def __unicode__(self): return self.title def get_absolute_url(self): return reverse('document_detail', args=(self.id, )) @property def name_long(self): if not self.title: return str(self.id) else: return '%s (%s)' % (self.title, self.id) def _render_thumbnail(self): from cStringIO import StringIO size = 200, 150 try: from PIL import Image, ImageOps except ImportError, e: logger.error( '%s: Pillow not installed, cannot generate thumbnails.' % e) return None try: # if wand is installed, than use it for pdf thumbnailing from wand import image except: wand_available = False else: wand_available = True if wand_available and self.extension and self.extension.lower( ) == 'pdf' and self.doc_file: logger.debug('Generating a thumbnail for document: {0}'.format( self.title)) with image.Image(filename=self.doc_file.path) as img: img.sample(*size) return img.make_blob('png') elif self.extension and self.extension.lower( ) in IMGTYPES and self.doc_file: img = Image.open(self.doc_file.path) img = ImageOps.fit(img, size, Image.ANTIALIAS) else: filename = finders.find('documents/{0}-placeholder.png'.format(self.extension), False) or \ finders.find('documents/generic-placeholder.png', False) if not filename: return None img = Image.open(filename) imgfile = StringIO() img.save(imgfile, format='PNG') return imgfile.getvalue()
class FullHistory(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.CharField(max_length=255) revision = models.PositiveIntegerField() content_object = generic.GenericForeignKey() action_time = models.DateTimeField(auto_now_add=True) _data = models.TextField(db_column='data') request = models.ForeignKey(Request, null=True, blank=True) site = models.ForeignKey(Site, default=Site.objects.get_current) action = models.CharField(max_length=1, choices=ACTIONS) info = models.TextField() objects = FullHistoryManager() def set_data(self, val): self._data = ENCODER.encode(val) def get_data(self): return simplejson.loads(self._data) data = property(get_data, set_data) def action_display(self): return dict(ACTIONS)[self.action] def user(self): ''' Returns the user entry responsible for this change May return User.DoesNotExist if the user was deleted ''' if self.request is None: return None return self.request.user() def create_info(self): ''' Generates a summary description of this history entry ''' user_name = u'(System)' if self.request: user_name = self.request.user_name ret = list() ret.append({ 'C': u'%s Created', 'U': u'%s Updated', 'D': u'%s Deleted', }[self.action] % user_name) if self.action == 'U': for key, value in self.data.items(): if not isinstance(value, tuple) or len(value) != 2: #fix for old admin continue ret.append( u'"%s" changed from [%s] to [%s]' % (key, unicode(value[0])[:50], unicode(value[1])[:50])) return '\n'.join(ret) def previous(self): ''' Retrieves the previous history entry for this object ''' return FullHistory.objects.get(content_type=self.content_type, object_id=self.object_id, revision=self.revision - 1) def next(self): ''' Retrieves the next history entry for this object ''' return FullHistory.objects.get(content_type=self.content_type, object_id=self.object_id, revision=self.revision + 1) def related_changes(self): ''' Returns a queryset of the changes that have also occurred with this change ''' if self.request: return FullHistory.objects.filter(request=self.request).exclude( pk=self.pk) return FullHistory.objects.none() def save(self, *args, **kwargs): if not self.pk: self.revision = len( FullHistory.objects.filter(content_type=self.content_type, object_id=self.object_id)) if not self.info: self.info = self.create_info() return super(FullHistory, self).save(*args, **kwargs) def __unicode__(self): return u'%s %s %s' % (self.content_type, self.object_id, self.action_time) objects = FullHistoryManager() class Meta: verbose_name_plural = _("full histories") get_latest_by = "revision" unique_together = (('revision', 'content_type', 'object_id'), )
class Model(models.Model): content_type = models.IntegerField() # should be ForeignKey object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id')
class Model(models.Model): content_type = models.ForeignKey( 'self') # should point to ContentType object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id')
class BanyanUserNotifications(models.Model): createdAt = models.DateTimeField(auto_now_add=True) user = models.ForeignKey(BanyanUser, related_name='notifications', null=False, blank=False, db_index=True) from_user = models.ForeignKey(BanyanUser, related_name='+', null=True, blank=True) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField(db_index=True) content_object = generic.GenericForeignKey('content_type', 'object_id') activity = models.ForeignKey('core.Activity', null=True, blank=True) description = models.CharField(max_length=500) ''' Notification types ''' LIKE_NOTIF = 'like' FOLLOW_NOTIF = 'follow' JOIN_NOTIF = 'join' STORY_STARTED_NOTIF = 'story_start' PIECE_ADDED_NOTIF = 'piece_add' VIEW_INVITE_NOTIF = 'view_inv' CONTRIB_INVITE_NOTIF = 'contrib_inv' NOTIFICATION_TYPE_CHOICES = ((LIKE_NOTIF, 'loves the piece'), (FOLLOW_NOTIF, 'is following'), (JOIN_NOTIF, 'has joined Banyan'), (STORY_STARTED_NOTIF, 'has started story'), (PIECE_ADDED_NOTIF, 'has added piece'), (VIEW_INVITE_NOTIF, 'has invited you to read'), (CONTRIB_INVITE_NOTIF, 'has invited you to contribute to')) type = models.CharField(max_length=20, choices=NOTIFICATION_TYPE_CHOICES, db_index=True) ''' Cache keys These are the cache keys related to this model ''' class Meta: unique_together = ('user', 'from_user', 'object_id', 'content_type', 'type') ordering = ['-createdAt'] def __unicode__(self): return u"{from_user} {verb} {object}".format( from_user=self.from_user.get_full_name(), verb=self.get_type_display(), object=unicode(self.content_object)) @classmethod def bulk_save_all(cls, notifications=[]): for notification in notifications: try: notification.save() except IntegrityError: continue except: logger.info( "accounts.models.BanyanUserNotifications::bulk_save_all error {} {} in creating notification" .format(sys.exc_info()[0], sys.exc_info()[1])) continue
class Activity(models.Model): """ Saving an activity """ CREATE = 1 UPDATE = 2 DELETE = 3 CLOSE = 4 COMMENT = 5 INVITE = 6 START = 7 ADMIN = 8 JOIN = 9 ACTION_CHOICES = ( (CREATE, _('created')), (UPDATE, _('updated')), (DELETE, _('deleted')), (CLOSE, _('closed')), (COMMENT, _('commented on')), (INVITE, _('invited')), (START, _('started')), (ADMIN, _('permissions')), (JOIN, _('joined')), ) actor = models.ForeignKey(User, related_name='activities') action = models.SmallIntegerField(_('action'), choices=ACTION_CHOICES) project = models.ForeignKey(Project, related_name='activities') time = models.DateTimeField(_('time'), auto_now_add=True) # Direct object object_id = models.PositiveIntegerField() content_type = models.ForeignKey(ContentType, related_name='activity_objects') content_object = generic.GenericForeignKey('content_type', 'object_id') # Indirect object indirect_object_id = models.PositiveIntegerField(blank=True, null=True) indirect_content_type = models.ForeignKey( ContentType, related_name='activity_indirect_objects', blank=True, null=True) indirect_content_object = generic.GenericForeignKey( 'indirect_content_type', 'indirect_object_id') objects = ActivityManager() def __unicode__(self): return self.humanize(admin=True).strip() def save(self, *args, **kwargs): """ When saving an activity e-mail notification to project members """ super(Activity, self).save(*args, **kwargs) # Send mail as notification if settings.EMAIL_NOTIFICATIONS is True: mail_data = self.prepare_mail() mail_data.send() class Meta: verbose_name = _('activity') verbose_name_plural = _('activities') ordering = ['-time'] @property def app_label(self): """ Returns the ``app_label`` so we can search for the templates. """ return self.content_type.app_label def _get_template_for_action(self, mail=''): """ Returns the template for this action """ options = [ '%(app_label)s/actions/%(action)s%(mail)s.txt' % { 'app_label': self.app_label, 'action': self.get_action_display().replace(' ', ''), 'mail': mail, }, '%(app_label)s/actions/generic%(mail)s.txt' % { 'app_label': self.app_label, 'mail': mail }, 'blactivity/generic.txt' ] t = loader.select_template(options) return t def humanize(self, admin=False, mail=False): """ Returns a sentence of the committed action """ if admin: template = loader.get_template('blactivity/admin.txt') elif mail: template = self._get_template_for_action('_mail') else: template = self._get_template_for_action() context = Context({ 'actor': self.actor.username, 'actor_id': self.actor.id, 'action': self.get_action_display(), 'project': self.project, 'project_id': self.project.id, 'time': self.time, 'object': self.content_object, 'object_id': self.object_id, 'object_type': self.content_type, 'indirect_object': self.indirect_content_object if self.indirect_object_id else None, 'indirect_object_id': self.indirect_object_id if self.indirect_object_id else None, 'MEDIA_URL': settings.MEDIA_URL, }) sentence = template.render(context) return sentence def prepare_mail(self): """ Prepare mass mail """ subject = "[%(project)s] New activity" % {'project': self.project} message = self.humanize(mail=True) from_email = settings.EMAIL_HOST_USER email = EmailMessage(subject, message, from_email, self.recipient_list()) email.content_subtype = "html" return email def recipient_list(self): """ Return list with all recipient for mail """ recipient_list = [] for member in self.project.members.all(): try: profile = member.get_profile() except: pass else: if profile.notifications: recipient_list.append(member.email) if len(recipient_list) > 0: return recipient_list else: return None
class MenuItem(MPTTModel): parent = TreeForeignKey('self', null=True, blank=True, related_name='children') label = models.CharField( _('label'), max_length=255, help_text="The display name on the web site.", ) slug = models.SlugField( _('slug'), unique=True, max_length=255, help_text="Unique identifier for this menu item (also CSS ID)") order = models.IntegerField( _('order'), choices=[(x, x) for x in xrange(0, 51)], ) is_enabled = models.BooleanField(default=True) link = models.CharField( _('link'), max_length=255, help_text= "The view of the page you want to link to, as a python path or the shortened URL name.", blank=True, ) content_type = models.ForeignKey( ContentType, null=True, blank=True, ) object_id = models.PositiveIntegerField( null=True, blank=True, ) content_object = generic.GenericForeignKey('content_type', 'object_id') href = models.CharField(_('href'), editable=False, max_length=255) objects = MenuItemManager() tree = TreeManager() class Meta: ordering = ('lft', 'tree_id') class MPTTMeta: order_insertion_by = ('order', ) def to_tree(self): cache_key = 'menu-tree-%s' % self.slug root = cache.get(cache_key) if not root: item = root = Item(self) descendents = self.get_descendants() for prev, curr, next in previous_current_next(descendents): previous_item = item item = Item(curr) if not prev or prev.level < curr.level: previous_item.add_child(item) elif prev and prev.level > curr.level: parent = previous_item while parent.node.level >= curr.level: parent = parent.parent parent.add_child(item) else: previous_item.parent.add_child(item) cache.set(cache_key, root) return root def save(self, *args, **kwargs): literal_url_prefixes = ('/', 'http://', 'https://') regex_url_prefixes = ('^', ) if self.link: if any([self.link.startswith(s) for s in literal_url_prefixes]): self.href = self.link elif any([self.link.startswith(s) for s in regex_url_prefixes]): self.href = '' # regex should not be used as an actual URL else: self.href = reverse(self.link) elif self.content_object: self.href = self.content_object.get_absolute_url() else: self.href = '' delete_cache() super(MenuItem, self).save(*args, **kwargs) def delete(self, *args, **kwargs): delete_cache() super(MenuItem, self).delete(*args, **kwargs) def __unicode__(self): return self.slug
class FreeThreadedComment(models.Model): """ A threaded comment which need not be associated with an instance of ``django.contrib.auth.models.User``. Instead, it requires minimally a name, and maximally a name, website, and e-mail address. It is given its hierarchy by a nullable relationship back on itself named ``parent``. This ``FreeThreadedComment`` supports several kinds of markup languages, including Textile, Markdown, and ReST. It also includes two Managers: ``objects``, which is the same as the normal ``objects`` Manager with a few added utility functions (see above), and ``public``, which has those same utility functions but limits the QuerySet to only those values which are designated as public (``is_public=True``). """ # Generic Foreign Key Fields content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField(_('object ID')) content_object = generic.GenericForeignKey() # Hierarchy Field parent = models.ForeignKey('self', null = True, blank=True, default = None, related_name='children') # User-Replacement Fields name = models.CharField(_('name'), max_length = 128) website = models.URLField(_('site'), blank = True) email = models.EmailField(_('e-mail address'), blank = True) # Date Fields date_submitted = models.DateTimeField(_('date/time submitted'), default = datetime.now) date_modified = models.DateTimeField(_('date/time modified'), default = datetime.now) date_approved = models.DateTimeField(_('date/time approved'), default=None, null=True, blank=True) # Meat n' Potatoes comment = models.TextField(_('comment')) markup = models.IntegerField(choices=MARKUP_CHOICES, default=DEFAULT_MARKUP, null=True, blank=True) # Status Fields is_public = models.BooleanField(_('is public'), default = True) is_approved = models.BooleanField(_('is approved'), default = False) # Extra Field ip_address = models.IPAddressField(_('IP address'), null=True, blank=True) objects = ThreadedCommentManager() public = PublicThreadedCommentManager() def __unicode__(self): if len(self.comment) > 50: return self.comment[:50] + "..." return self.comment[:50] def save(self, **kwargs): if not self.markup: self.markup = DEFAULT_MARKUP self.date_modified = datetime.now() if not self.date_approved and self.is_approved: self.date_approved = datetime.now() super(FreeThreadedComment, self).save() def get_content_object(self, **kwargs): """ Wrapper around the GenericForeignKey due to compatibility reasons and due to ``list_display`` limitations. """ return self.content_object def get_base_data(self, show_dates=True): """ Outputs a Python dictionary representing the most useful bits of information about this particular object instance. This is mostly useful for testing purposes, as the output from the serializer changes from run to run. However, this may end up being useful for JSON and/or XML data exchange going forward and as the serializer system is changed. """ markup = "plaintext" for markup_choice in MARKUP_CHOICES: if self.markup == markup_choice[0]: markup = markup_choice[1] break to_return = { 'content_object' : self.content_object, 'parent' : self.parent, 'name' : self.name, 'website' : self.website, 'email' : self.email, 'comment' : self.comment, 'is_public' : self.is_public, 'is_approved' : self.is_approved, 'ip_address' : self.ip_address, 'markup' : force_unicode(markup), } if show_dates: to_return['date_submitted'] = self.date_submitted to_return['date_modified'] = self.date_modified to_return['date_approved'] = self.date_approved return to_return class Meta: ordering = ('-date_submitted',) verbose_name = _("Free Threaded Comment") verbose_name_plural = _("Free Threaded Comments") get_latest_by = "date_submitted"
class Value(models.Model): ''' Putting the **V** in *EAV*. This model stores the value for one particular :class:`Attribute` for some entity. As with most EAV implementations, most of the columns of this model will be blank, as onle one *value_* field will be used. Example: >>> import eav >>> from django.contrib.auth.models import User >>> eav.register(User) >>> u = User.objects.create(username='******') >>> a = Attribute.objects.create(name='Favorite Drink', datatype='text', ... slug='fav_drink') >>> Value.objects.create(entity=u, attribute=a, value_text='red bull') <Value: crazy_dev_user - Favorite Drink: "red bull"> ''' entity_ct = models.ForeignKey(ContentType, related_name='value_entities') entity_id = models.IntegerField() entity = generic.GenericForeignKey(ct_field='entity_ct', fk_field='entity_id') value_text = models.TextField(blank=True, null=True) value_float = models.FloatField(blank=True, null=True) value_int = models.IntegerField(blank=True, null=True) value_date = models.DateTimeField(blank=True, null=True) value_bool = models.NullBooleanField(blank=True, null=True) value_enum = models.ForeignKey(EnumValue, blank=True, null=True, related_name='eav_values') generic_value_id = models.IntegerField(blank=True, null=True) generic_value_ct = models.ForeignKey(ContentType, blank=True, null=True, related_name='value_values') value_object = generic.GenericForeignKey(ct_field='generic_value_ct', fk_field='generic_value_id') created = models.DateTimeField(_(u"created"), default=datetime.now) modified = models.DateTimeField(_(u"modified"), auto_now=True) attribute = models.ForeignKey(Attribute, db_index=True, verbose_name=_(u"attribute")) def save(self, *args, **kwargs): ''' Validate and save this value ''' self.full_clean() super(Value, self).save(*args, **kwargs) def clean(self): ''' Raises ``ValidationError`` if this value's attribute is *TYPE_ENUM* and value_enum is not a valid choice for this value's attribute. ''' if self.attribute.datatype == Attribute.TYPE_ENUM and \ self.value_enum: if self.value_enum not in self.attribute.enum_group.enums.all(): raise ValidationError(_(u"%(choice)s is not a valid " \ u"choice for %s(attribute)") % \ {'choice': self.value_enum, 'attribute': self.attribute}) def _get_value(self): ''' Return the python object this value is holding ''' return getattr(self, 'value_%s' % self.attribute.datatype) def _set_value(self, new_value): ''' Set the object this value is holding ''' setattr(self, 'value_%s' % self.attribute.datatype, new_value) value = property(_get_value, _set_value) def __unicode__(self): return u"%s - %s: \"%s\"" % (self.entity, self.attribute.name, self.value)
class XaploadImageRel(models.Model): image = models.ImageField(upload_to='media/') content_type = models.ForeignKey(ContentType) object_id = models.PositiveSmallIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id')
class Model(models.Model): rel = generic.GenericRelation('Model') content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id')
class Feature(models.Model): """Model used for representing user-generated features ====================== ============================================== Attribute Description ====================== ============================================== ``user`` Creator ``name`` Name of the object ``date_created`` When it was created ``date_modified`` When it was last updated. ====================== ============================================== """ user = models.ForeignKey(User, related_name="%(app_label)s_%(class)s_related") name = models.CharField(verbose_name="Name", max_length="255") date_created = models.DateTimeField(auto_now_add=True, verbose_name="Date Created") date_modified = models.DateTimeField(auto_now=True, verbose_name="Date Modified") sharing_groups = models.ManyToManyField( Group, editable=False, blank=True, null=True, verbose_name="Share with the following groups", related_name="%(app_label)s_%(class)s_related") content_type = models.ForeignKey( ContentType, blank=True, null=True, related_name="%(app_label)s_%(class)s_related") object_id = models.PositiveIntegerField(blank=True, null=True) collection = generic.GenericForeignKey('content_type', 'object_id') objects = ShareableGeoManager() def __unicode__(self): return u"%s_%s" % (self.model_uid(), self.pk) def __repr__(self): return u"%s_%s" % (self.model_uid(), self.pk) class Meta: abstract = True ''' Note on keyword args rerun and form: These are extracted from kwargs so that they will not cause an unexpected keyword argument error during call to super.save. (They are used in the Analysis model save method, but become superfluous here.) ''' def save(self, rerun=True, form=None, *args, **kwargs): super(Feature, self).save(*args, **kwargs) # Call the "real" save() method if form is not None: form.save_m2m() @models.permalink def get_absolute_url(self): return ('%s_resource' % (self.get_options().slug, ), (), { 'uid': self.uid }) @classmethod def get_options(klass): """ Returns model class Options object """ return get_model_options(klass.__name__) @classmethod def css(klass): """ Specifies the CSS for representing features in kmltree, specifically the icon Works one of two ways: 1. Use the icon_url Option and this default css() classmethod 2. Override the css() classmethod for more complex cases """ url = klass.get_options().icon_url if url: if not url.startswith("/") and not url.startswith("http://"): url = settings.MEDIA_URL + url return """ li.%s > .icon { background: url("%s") no-repeat scroll 0 0 transparent ! important; display:inline ! important; } div.%s > .goog-menuitem-content { background: url("%s") no-repeat scroll 0 0 transparent !important; display: block !important; padding-left: 22px; position: relative; left: -22px; height: 16px; } """ % (klass.model_uid(), url, klass.model_uid(), url) @property def options(self): return get_model_options(self.__class__.__name__) @classmethod def model_uid(klass): """ class method providing the uid for the model class. """ ct = ContentType.objects.get_for_model(klass) return "%s_%s" % (ct.app_label, ct.model) @property def hash(self): """ For caching. This string represents a hash of all attributes that may influence reporting results. i.e. if this property changes, reports for the feature get rerun. """ important = "%s%s" % (self.date_modified, self.uid) return important.__hash__() @property def uid(self): """ Unique identifier for this feature. """ if not self.pk: raise Exception( 'Trying to get uid for feature class that is not yet saved!') return "%s_%s" % ( self.model_uid(), self.pk, ) @property def kml_safe(self): """ A safety valve for kmlapp... If one feature's .kml property fails, it won't bring down the entire request. This property is never to be overridden! """ try: return self.kml except Exception as e: try: logger.error("%s .kml property is failing: \n%s\n" % (self.uid, e.message)) except: # just in case logging or the uid property are fubar print ".kml is failing on something" # Create a fallback KML placemark so it doesn't just disappear return """ <Placemark id="%s"> <visibility>0</visibility> <name>%s (KML Error)</name> <description>Error Details ... %s ... If the problem persists, please contact us.</description> </Placemark> """ % (self.uid, self.name, e.message) def add_to_collection(self, collection): """ Add feature to specified FeatureCollection """ assert issubclass(collection.__class__, FeatureCollection) assert self.__class__ in collection.get_options().get_valid_children() assert self.user == collection.user self.collection = collection self.save(rerun=False) def remove_from_collection(self): """ Remove feature from FeatureCollection """ collection = self.collection self.collection = None self.save(rerun=False) if collection: collection.save(rerun=True) def share_with(self, groups, append=False): """ Share this feature with the specified group/groups. Owner must be a member of the group/groups. Group must have 'can_share' permissions else an Exception is raised """ if not append: # Don't append to existing groups; Wipe the slate clean # Note that this is the default behavior self.sharing_groups.clear() if groups is None or groups == []: # Nothing to do here return True if isinstance(groups, Group): # Only a single group was provided, make a 1-item list groups = [groups] for group in groups: assert isinstance(group, Group) # Check that the group to be shared with has appropos permissions assert group in self.user.groups.all() try: gp = group.permissions.get(codename='can_share_features') except: raise Exception("The group you are trying to share with " "does not have can_share permission") self.sharing_groups.add(group) self.save(rerun=False) return True def is_viewable(self, user): """ Is this feauture viewable by the specified user? Either needs to own it or have it shared with them. returns : Viewable(boolean), HttpResponse """ # First, is the user logged in? if user.is_anonymous() or not user.is_authenticated(): try: obj = self.__class__.objects.shared_with_user(user).get( pk=self.pk) return True, HttpResponse( "Object shared with public, viewable by anonymous user", status=202) except self.__class__.DoesNotExist: # Unless the object is publicly shared, we won't give away anything return False, HttpResponse('You must be logged in', status=401) # Does the user own it? if self.user == user: return True, HttpResponse("Object owned by user", status=202) # Next see if its shared with the user try: # Instead having the sharing logic here, use the shared_with_user # We need it to return querysets so no sense repeating that logic obj = self.__class__.objects.shared_with_user(user).get(pk=self.pk) return True, HttpResponse("Object shared with user", status=202) except self.__class__.DoesNotExist: return False, HttpResponse("Access denied", status=403) return False, HttpResponse("Server Error in feature.is_viewable()", status=500) def copy(self, user=None): """ Returns a copy of this feature, setting the user to the specified owner. Copies many-to-many relations """ # Took this code almost verbatim from the mpa model code. # TODO: Test if this method is robust, and evaluate alternatives like # that described in django ticket 4027 # http://code.djangoproject.com/ticket/4027 the_feature = self # Make an inventory of all many-to-many fields in the original feature m2m = {} for f in the_feature._meta.many_to_many: m2m[f.name] = the_feature.__getattribute__(f.name).all() # The black magic voodoo way, # makes a copy but relies on this strange implementation detail of # setting the pk & id to null # An alternate, more explicit way, can be seen at: # http://blog.elsdoerfer.name/2008/09/09/making-a-copy-of-a-model-instance the_feature.pk = None the_feature.id = None the_feature.save(rerun=False) the_feature.name = the_feature.name + " (copy)" # Restore the many-to-many fields for fname in m2m.keys(): for obj in m2m[fname]: the_feature.__getattribute__(fname).add(obj) # Reassign User the_feature.user = user # Clear everything else the_feature.sharing_groups.clear() the_feature.remove_from_collection() the_feature.save(rerun=False) return the_feature
class TaggedItem(models.Model): # no content_type field object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey()
class DummyGenericForeignKeyModel(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id')
class TaggedItem(models.Model): content_type = models.ForeignKey(ContentType) # missing object_id field content_object = generic.GenericForeignKey()
class Entry(models.Model): """ Entry model for the logger app. This is a model that creates a generic relationship between it and other models for creating a "recent activity" history. It requires a global list variable in the settings file named `LOGGER_MODELS`, with each value in the list being in the format of `appname.model`. For example, if I have an app called `blog` and a model called `Post` that I want to include in my tumblelog, I would add this to my settings file. `LOGGER_MODELS = ['blog.post']` This model tries three methods for displaying the title, description, and title and description together on the site and in the RSS feed, and these methods are called `title`, `description` and `render`. It is done this way because of the way RSS displays the "title" and "description" separately. If we only did a render, the RSS description of the entry would include both the title and the description, which would be undesired. There are three ways of usings these functions. 1. Write a custom method in your model. It looks for this first, and if it doesn't find it, it moves on. It looks for a method called `get_title` when getting the title, `get_description` for showing the description, and `render` for displaying both the title and description. For example, if I have a blog app with a `Post` model, I can create a `get_title`, `get_description`, and/or `render` method in my model and this model will call it. 2. Use a custom template. Inside of one of your template directories, you should have a folder for your app that is the name of your app. Inside of this directory, create a file for the `title`, `description`, and `render` methods to use. These should be named `<model_name>_title.html`, `<model_name>_description.html`, and `<model_name>_render.html` respectively. For example, if I have created a blog app with a `Post` model, and I want to create a custom template for displaying the description, I would create a file `logger/post_description.html`. For the `render` method, you can use the `<model_name>_render.html`, or you can create a generic render template to be use by default. Name it `render.html` and put it in a folder called `logger` somewhere on your template path, which will be `logger/render.html`. 3. Use the default. This model will create generic title and description values. If nothing is set for the title or description (either with a custom template or custom method), it will use the string representation of that object. Note: refer to the the second option above to see about creating a default template for the `render` method. """ action = models.ForeignKey(Action, blank=True, null=True) created_at = models.DateTimeField() content_type = models.ForeignKey(ContentType, related_name='kate_logentry') object_id = models.PositiveIntegerField() public = models.BooleanField(default=True) content_object = generic.GenericForeignKey() objects = EntryManager() class Meta: ordering = ["-created_at"] verbose_name_plural = 'Log Entries' get_latest_by = 'created_at' # These methods below are for displaying the entry, both on the # site and in the RSS feeds. Since the RSS feeds require that # we display the Title and Description of an item separately, # we have to have separate methods for them. We also have a # render method that will display a custom render template or # use get_title and get_description to display one. def _get_title(self): """ Here we get the title of the `Entry`. First, check for a `get_title` method, then try to use a template called `<app_label>/<model_name>_title.html`. If it finds neither, it will use the object's string representation. """ # First, see if there is a title method on the content object if hasattr(self.content_object, 'get_title'): return self.content_object.get_title() # Next, see if there is template called <app_label>/<model_name>_title.html try: return render_to_string( "%s/%s_title.html" % (self.content_type.app_label, self.content_type.name), {'obj': self.content_object}) except TemplateDoesNotExist: # Finally, just use the string representation of the object t = Template('{{ obj }}') c = Context({'obj': self.content_object}) return t.render(c) title = property(_get_title) def _get_description(self): """ Here we get the description of the `Entry`. First, check for a `get_title` method, then try to use a template called `<app_label>/<model_name>_title.html`. If it finds neither, it will use the object's string representation. """ # First, see if there is a description method on the content object if hasattr(self.content_object, 'get_description'): return self.content_object.get_description() # Next, see if there is template called <app_label>/<model_name>_description.html try: return render_to_string( "%s/%s_description.html" % (self.content_type.app_label, self.content_type.name), {'obj': self.content_object}) except TemplateDoesNotExist: # Finally, just use the string representation of the object t = Template('{{ obj }}') c = Context({'obj': self.content_object}) return t.render(c) description = property(_get_description) def render(self): """ This first looks for a render file under `<app_label>/<model_name>_title.html` and tries to use that to display this model. If that template does not exist, it looks for a template called `djumblelog/render.html`, which exists in this app but can be replaced within another template directory. """ # First, see if there is a render method on the content object if hasattr(self.content_object, 'render'): return self.content_object.render() # Next, see if there is a template called <app_label>/<model_name>_render.html try: return render_to_string( "%s/%s_render.html" % (self.content_type.app_label, self.content_type.name), { 'obj': self.content_object, 'title': self.title, 'description': self.description }) except TemplateDoesNotExist: # Finally, we use our default `render.html` template to display the entry return render_to_string( "djumblelog/render.html", { 'obj': self.content_object, 'title': self.title, 'description': self.description }) def get_absolute_url(self): """Use the `get_absolute_url` of the content object for our `Entry`""" return self.content_object.get_absolute_url() def save(self, *args, **kwargs): if not self.id: self.created_at = datetime.now() super(Entry, self).save(*args, **kwargs) def __unicode__(self): return u"%s: %s" % (self.content_type.model_class().__name__, self.content_object)
class TaggedItem(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey()
class Order(models.Model): """ An order is created when products have been sold. **Attributes:** number The unique order number of the order, which is the reference for the customer. voucher_number, voucher_value, voucher_tax Storing this information here assures that we have it all time, even when the involved voucher will be deleted. requested_delivery_date A buyer requested delivery date (e.g. for a florist to deliver flowers on a specific date) pay_link A link to re-pay the order (e.g. for PayPal) invoice_address_id The invoice address of the order (this is not a FK because of circular imports). shipping_address_id The shipping address of the order (this is not a FK because of circular imports). """ number = models.CharField(max_length=30) user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True) session = models.CharField(_(u"Session"), blank=True, max_length=100) created = models.DateTimeField(_(u"Created"), auto_now_add=True) state = models.PositiveSmallIntegerField(_(u"State"), choices=ORDER_STATES, default=SUBMITTED) state_modified = models.DateTimeField(_(u"State modified"), auto_now_add=True) price = models.FloatField(_(u"Price"), default=0.0) tax = models.FloatField(_(u"Tax"), default=0.0) customer_firstname = models.CharField(_(u"firstname"), max_length=50) customer_lastname = models.CharField(_(u"lastname"), max_length=50) customer_email = models.CharField(_(u"email"), max_length=75) sa_content_type = models.ForeignKey(ContentType, related_name="order_shipping_address") sa_object_id = models.PositiveIntegerField() shipping_address = generic.GenericForeignKey('sa_content_type', 'sa_object_id') ia_content_type = models.ForeignKey(ContentType, related_name="order_invoice_address") ia_object_id = models.PositiveIntegerField() invoice_address = generic.GenericForeignKey('ia_content_type', 'ia_object_id') shipping_method = models.ForeignKey(ShippingMethod, verbose_name=_(u"Shipping Method"), blank=True, null=True) shipping_price = models.FloatField(_(u"Shipping Price"), default=0.0) shipping_tax = models.FloatField(_(u"Shipping Tax"), default=0.0) payment_method = models.ForeignKey(PaymentMethod, verbose_name=_(u"Payment Method"), blank=True, null=True) payment_price = models.FloatField(_(u"Payment Price"), default=0.0) payment_tax = models.FloatField(_(u"Payment Tax"), default=0.0) account_number = models.CharField(_(u"Account number"), blank=True, max_length=30) bank_identification_code = models.CharField(_(u"Bank identication code"), blank=True, max_length=30) bank_name = models.CharField(_(u"Bank name"), blank=True, max_length=100) depositor = models.CharField(_(u"Depositor"), blank=True, max_length=100) voucher_number = models.CharField(_(u"Voucher number"), blank=True, max_length=100) voucher_price = models.FloatField(_(u"Voucher value"), default=0.0) voucher_tax = models.FloatField(_(u"Voucher tax"), default=0.0) message = models.TextField(_(u"Message"), blank=True) pay_link = models.TextField(_(u"pay_link"), blank=True) uuid = models.CharField(max_length=50, editable=False, unique=True, default=get_unique_id_str) requested_delivery_date = models.DateTimeField(_(u"Delivery Date"), null=True, blank=True) class Meta: ordering = ("-created", ) def __unicode__(self): return "%s (%s %s)" % (self.created.strftime("%x %X"), self.customer_firstname, self.customer_lastname) def get_pay_link(self, request): """ Returns a pay link for the selected payment method. """ if self.payment_method.module: payment_class = lfs.core.utils.import_symbol(self.payment_method.module) payment_instance = payment_class(request=request, order=self) try: return payment_instance.get_pay_link() except AttributeError: return "" else: return "" def can_be_paid(self): return self.state in (SUBMITTED, PAYMENT_FAILED, PAYMENT_FLAGGED) def get_name(self): order_name = "" for order_item in self.items.all(): if order_item.product is not None: order_name = order_name + order_item.product.get_name() + ", " order_name.strip(', ') return order_name
class CI(TimeTrackable): uid = models.CharField( max_length=100, unique=True, verbose_name=_("CI UID"), null=True, blank=True, ) # not required, since auto-save name = models.CharField(max_length=256, verbose_name=_("CI name")) business_service = models.BooleanField( verbose_name=_("Business service"), default=False, ) technical_service = models.BooleanField( verbose_name=_("Technical service"), default=True, ) pci_scope = models.BooleanField(default=False) layers = models.ManyToManyField( CILayer, verbose_name=_("layers containing given CI")) barcode = models.CharField( verbose_name=_("barcode"), max_length=255, unique=True, null=True, default=None, ) content_type = models.ForeignKey( ContentType, verbose_name=_("content type"), null=True, blank=True, ) object_id = models.PositiveIntegerField( verbose_name=_("object id"), null=True, blank=True, ) content_object = generic.GenericForeignKey('content_type', 'object_id') state = models.IntegerField( max_length=11, choices=CI_STATE_TYPES(), default=CI_STATE_TYPES.INACTIVE.id, verbose_name=_("state"), ) status = models.IntegerField( max_length=11, choices=CI_STATUS_TYPES(), default=CI_STATUS_TYPES.REFERENCE.id, verbose_name=_("status"), ) type = models.ForeignKey(CIType) zabbix_id = models.CharField( null=True, blank=True, max_length=30, ) relations = models.ManyToManyField("self", symmetrical=False, through='CIRelation') added_manually = models.BooleanField(default=False) owners = models.ManyToManyField( 'CIOwner', through='CIOwnership', verbose_name=_("configuration item owners"), ) @property def url(self): return '/cmdb/ci/view/{0}'.format(self.id) @classmethod def get_duplicate_names(cls): dupes = cls.objects.values('name').distinct().annotate( models.Count('id'), ).filter(id__count__gt=1) cis = cls.objects.filter(name__in=[dupe['name'] for dupe in dupes ], ).order_by('name') # If I try to return the groupby itself the groups are empty for name, cis in it.groupby(cis, lambda x: x.name): yield name, list(cis) class Meta: unique_together = ('content_type', 'object_id') def __unicode__(self): return "%s (%s)" % (self.name, self.type) @classmethod def get_uid_by_content_object(cls, obj): prefix = CIContentTypePrefix.get_prefix_by_object(obj, '') return '%s-%s' % (prefix, obj.id) def get_jira_display(self): return "%(name)s %(uid)s - #%(barcode)s type: %(type)s" % (dict( name=self.name, uid=self.uid, barcode=self.barcode or '', type=self.type)) def get_service(self): """ Business / Organisation Unit Layer Venture 1-> Venture 2-> Venture Role -> Host -> Iterate upside, stop on first Venture in Business Layer """ def get_technical_owners(self): if self.content_object and getattr(self.content_object, 'venture', None): return list([ unicode(x) for x in self.content_object.venture.technical_owners() ] or ['-']) elif self.content_object and self.type.id == CI_TYPES.VENTURE.id: return list( [unicode(x) for x in self.content_object.technical_owners()] or ['-']) else: return ['-'] @classmethod def get_cycle(cls): allci = CI.objects.all().values('pk') relations = CIRelation.objects.all().values('parent_id', 'child_id') cis = [x['pk'] for x in allci] rel = [(x['parent_id'], x['child_id']) for x in relations] cycle = cls.has_cycle(cis, rel) return cycle @classmethod def has_cycle(cls, nodes, edges): gr = digraph() gr.add_nodes(nodes) for edge in edges: gr.add_edge(edge) return find_cycle(gr) @classmethod def get_by_content_object(self, content_object): # find CI using his content object prefix = CIContentTypePrefix.get_prefix_by_object(content_object, None) if not prefix: # fixtures not loaded, or content type not registered in CMDB. # Skip checking. return None try: ci = CI.objects.get(uid='%s-%s' % (prefix, content_object.id)) except CI.DoesNotExist: ci = None return ci @models.permalink def get_absolute_url(self): return "/cmdb/ci/view/%i" % self.id def save(self, user=None, *args, **kwargs): self.saving_user = user return super(CI, self).save(*args, **kwargs) def _get_related(self, self_field, other_field): """Iterate over the related objects. :param first_field: The field on relation that points to this CI :param second_field: The field on relation that points to other CI """ for relation in getattr(self, self_field).all(): yield getattr(relation, other_field) def get_parents(self): return self._get_related(self_field='child', other_field='parent') def get_children(self): return self._get_related(self_field='parent', other_field='child')