def setup_generic_relations(model_class): """ Set up GenericRelations for actionable models. """ Action = apps.get_model('actstream', 'action') if Action is None: raise RegistrationError( 'Unable get actstream.Action. Potential circular imports ' 'in initialisation. Try moving actstream app to come after the ' 'apps which have models to register in the INSTALLED_APPS setting.' ) related_attr_name = 'related_query_name' related_attr_value = 'actions_with_%s' % label(model_class) relations = {} for field in ('actor', 'target', 'action_object'): attr = '%s_actions' % field attr_value = '%s_as_%s' % (related_attr_value, field) kwargs = { 'content_type_field': '%s_content_type' % field, 'object_id_field': '%s_object_id' % field, related_attr_name: attr_value } rel = GenericRelation('actstream.Action', **kwargs) rel.contribute_to_class(model_class, attr) relations[field] = rel # @@@ I'm not entirely sure why this works setattr(Action, attr_value, None) return relations
def genericm2m_inlineformset_factory(source_model, model, form=forms.ModelForm, formset=GenericM2MInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=True, can_delete=True, max_num=None, formfield_callback=None): """ Returns an ``InlineFormSet`` for the given kwargs. You must provide ``fk_name`` if ``model`` has more than one ``ForeignKey`` to ``source_model``. """ fk = GenericRelation( source_model, verbose_name='source', related_query_name=fk_name or 'related') fk.name = 'source' kwargs = { 'form': form, 'formfield_callback': formfield_callback, 'formset': GenericM2MInlineFormSet, 'extra': extra, 'can_delete': can_delete, 'can_order': can_order, 'fields': fields, 'exclude': exclude, 'max_num': max_num, } FormSet = forms.models.modelformset_factory(model, **kwargs) # NOQA FormSet.fk = fk FormSet.source_type_field = 'source_type' return FormSet
def post_through_setup(self, cls): if RelatedObject is not None: # Django < 1.8 self.related = RelatedObject(cls, self.model, self) self.use_gfk = ( self.through is None or issubclass(self.through, CommonGenericTaggedItemBase) ) # rel.to renamed to remote_field.model in Django 1.9 if VERSION >= (1, 9): if not self.remote_field.model: self.remote_field.model = self.through._meta.get_field("tag").remote_field.model else: if not self.rel.to: self.rel.to = self.through._meta.get_field("tag").rel.to if RelatedObject is not None: # Django < 1.8 self.related = RelatedObject(self.through, cls, self) if self.use_gfk: tagged_items = GenericRelation(self.through) tagged_items.contribute_to_class(cls, 'tagged_items') for rel in cls._meta.local_many_to_many: if rel == self or not isinstance(rel, TaggableManager): continue if rel.through == self.through: raise ValueError('You can\'t have two TaggableManagers with the' ' same through model.')
def post_through_setup(self, cls): self.use_gfk = ( self.through is None ) self.rel.to = self.through._meta.get_field("group").rel.to if RelatedObject is not None: self.related = RelatedObject(self.through, cls, self) if self.use_gfk: groups = GenericRelation(self.through) groups.contribute_to_class(cls, "groups")
def contribute_to_class(model): """ Adds a 'maat_ranking' attribute to each instance of model. The attribute is a generic relation to MaatRanking, used by the handler to retrieve the ordered queryset. """ try: generic_relation = GenericRelation(MaatRanking) except TypeError: # Django < 1.7 generic_relation = GenericRelation(MaatRanking) generic_relation.contribute_to_class(model, 'maat_ranking')
def _attach_generic_relation(self): ''' Set up the generic relation for the entity ''' rel_name = self.config_cls.generic_relation_related_name or \ 'entity' gr_name = self.config_cls.generic_relation_attr.lower() generic_relation = GenericRelation(Value, object_id_field='entity_id', content_type_field='entity_ct', related_query_name=rel_name) generic_relation.contribute_to_class(self.model_cls, gr_name)
def _inject_generic_relation(qs_or_model): if isinstance(qs_or_model, models.Model): model = qs_or_model elif isinstance(qs_or_model, models.QuerySet): model = qs_or_model.model else: raise TypeError('Can`t inject generic relation') try: model._meta.get_field(MODEL_ATTR_FIELD_NAME) except FieldDoesNotExist: generic_relation = GenericRelation( Attribute, related_query_name=MODEL_ATTR_FIELD_NAME) generic_relation.contribute_to_class(model, MODEL_ATTR_FIELD_NAME)
def post_through_setup(self, cls): self.related = RelatedObject(cls, self.model, self) self.use_gfk = ( self.through is None or issubclass(self.through, GenericTaggedItemBase) ) self.rel.to = self.through._meta.get_field("tag").rel.to self.related = RelatedObject(self.through, cls, self) if self.use_gfk: tagged_items = GenericRelation(self.through) tagged_items.contribute_to_class(cls, 'tagged_items') for rel in cls._meta.local_many_to_many: if rel == self or not isinstance(rel, TaggableManager): continue if rel.through == self.through: raise ValueError('You can\'t have two TaggableManagers with the' ' same through model.')
def create_link(linkable_model, linked_model, linkable_link_name=None, verbose_name=None, verbose_name_plural=None): class LinkedModel(object): def __init__(self, model, link_name=None, verbose_name=None, verbose_name_plural=None, reverse_link_name=None): self.model = model self.link_name = link_name self.reverse_link_name = reverse_link_name self.verbose_name = verbose_name self.verbose_name_plural = verbose_name_plural if issubclass(linkable_model, ManyLinkableModel): get_link_name = get_plural elif issubclass(linkable_model, OneLinkableModel): get_link_name = get_singular if linkable_link_name is None: linkable_link_name = get_link_name(linked_model) if verbose_name is None: verbose_name = linked_model._meta.verbose_name if verbose_name_plural is None: verbose_name_plural = linked_model._meta.verbose_name_plural linked_link_name = get_plural(linkable_model) if issubclass(linkable_model, ManyLinkableModel): field = models.ManyToManyField(linkable_model, related_name=linkable_link_name) setattr(linked_model, linked_link_name, field) field.contribute_to_class(linked_model, linked_link_name) elif issubclass(linkable_model, OneLinkableModel): linked_link_name = get_singular(linkable_model)+"_set" field = GenericRelation(linkable_model, related_query_name=linkable_link_name) setattr(linked_model, linked_link_name, field) field.contribute_to_class(linked_model, linked_link_name) setattr(linkable_model, linkable_link_name, property( fget=lambda x: linkable_model.get_related(x), fset=lambda x, y: linkable_model.set_related(x, y))) if not hasattr(linkable_model, "_LINKS") or linkable_model._LINKS is None: setattr(linkable_model, "_LINKS", dict()) linkable_model._LINKS[linkable_link_name] = LinkedModel( linked_model, link_name=linkable_link_name, verbose_name=verbose_name, verbose_name_plural=verbose_name_plural, reverse_link_name=linked_link_name) return linkable_model
def setup_generic_relations(model_class): """ 注册过的model_class实例可以如此操作: instance.actor_actions.filter(...) instance.target_actions.filter(...) instance.relative_actions.filter(...) """ from qevent.models import Action related_attr_value = 'actions_with_%s' % label(model_class) # relations = {} for field in ('actor', 'target', 'relative'): attr_value = '%s_as_%s' % (related_attr_value, field) rel = GenericRelation(Action, content_type_field='%s_type' % field, object_id_field='%s_object_id' % field, related_query_name=attr_value, ) rel.contribute_to_class(model_class, '%s_actions' % field) relations[field] = rel # setattr(Action, attr_value, None) return relations
class VLAN(ChangeLoggedModel, CustomFieldModel): """ A VLAN is a distinct layer two forwarding domain identified by a 12-bit integer (1-4094). Each VLAN must be assigned to a Site, however VLAN IDs need not be unique within a Site. A VLAN may optionally be assigned to a VLANGroup, within which all VLAN IDs and names but be unique. Like Prefixes, each VLAN is assigned an operational status and optionally a user-defined Role. A VLAN can have zero or more Prefixes assigned to it. """ site = models.ForeignKey( to='dcim.Site', on_delete=models.PROTECT, related_name='vlans', blank=True, null=True ) group = models.ForeignKey( to='ipam.VLANGroup', on_delete=models.PROTECT, related_name='vlans', blank=True, null=True ) vid = models.PositiveSmallIntegerField( verbose_name='ID', validators=[MinValueValidator(1), MaxValueValidator(4094)] ) name = models.CharField( max_length=64 ) tenant = models.ForeignKey( to='tenancy.Tenant', on_delete=models.PROTECT, related_name='vlans', blank=True, null=True ) status = models.CharField( max_length=50, choices=VLANStatusChoices, default=VLANStatusChoices.STATUS_ACTIVE ) role = models.ForeignKey( to='ipam.Role', on_delete=models.SET_NULL, related_name='vlans', blank=True, null=True ) description = models.CharField( max_length=100, blank=True ) custom_field_values = GenericRelation( to='extras.CustomFieldValue', content_type_field='obj_type', object_id_field='obj_id' ) tags = TaggableManager(through=TaggedItem) csv_headers = ['site', 'group_name', 'vid', 'name', 'tenant', 'status', 'role', 'description'] clone_fields = [ 'site', 'group', 'tenant', 'status', 'role', 'description', ] STATUS_CLASS_MAP = { 'active': 'primary', 'reserved': 'info', 'deprecated': 'danger', } class Meta: ordering = ('site', 'group', 'vid', 'pk') # (site, group, vid) may be non-unique unique_together = [ ['group', 'vid'], ['group', 'name'], ] verbose_name = 'VLAN' verbose_name_plural = 'VLANs' def __str__(self): return self.display_name or super().__str__() def get_absolute_url(self): return reverse('ipam:vlan', args=[self.pk]) def clean(self): # Validate VLAN group if self.group and self.group.site != self.site: raise ValidationError({ 'group': "VLAN group must belong to the assigned site ({}).".format(self.site) }) def to_csv(self): return ( self.site.name if self.site else None, self.group.name if self.group else None, self.vid, self.name, self.tenant.name if self.tenant else None, self.get_status_display(), self.role.name if self.role else None, self.description, ) @property def display_name(self): if self.vid and self.name: return "{} ({})".format(self.vid, self.name) return None def get_status_class(self): return self.STATUS_CLASS_MAP[self.status] def get_members(self): # Return all interfaces assigned to this VLAN return Interface.objects.filter( Q(untagged_vlan_id=self.pk) | Q(tagged_vlans=self.pk) ).distinct()
class Service(ChangeLoggedModel, CustomFieldModel): """ A Service represents a layer-four service (e.g. HTTP or SSH) running on a Device or VirtualMachine. A Service may optionally be tied to one or more specific IPAddresses belonging to its parent. """ device = models.ForeignKey( to='dcim.Device', on_delete=models.CASCADE, related_name='services', verbose_name='device', null=True, blank=True ) virtual_machine = models.ForeignKey( to='virtualization.VirtualMachine', on_delete=models.CASCADE, related_name='services', null=True, blank=True ) name = models.CharField( max_length=30 ) protocol = models.CharField( max_length=50, choices=ServiceProtocolChoices ) port = models.PositiveIntegerField( validators=[MinValueValidator(SERVICE_PORT_MIN), MaxValueValidator(SERVICE_PORT_MAX)], verbose_name='Port number' ) ipaddresses = models.ManyToManyField( to='ipam.IPAddress', related_name='services', blank=True, verbose_name='IP addresses' ) description = models.CharField( max_length=100, blank=True ) custom_field_values = GenericRelation( to='extras.CustomFieldValue', content_type_field='obj_type', object_id_field='obj_id' ) tags = TaggableManager(through=TaggedItem) csv_headers = ['device', 'virtual_machine', 'name', 'protocol', 'description'] class Meta: ordering = ('protocol', 'port', 'pk') # (protocol, port) may be non-unique def __str__(self): return '{} ({}/{})'.format(self.name, self.port, self.get_protocol_display()) def get_absolute_url(self): return reverse('ipam:service', args=[self.pk]) @property def parent(self): return self.device or self.virtual_machine def clean(self): # A Service must belong to a Device *or* to a VirtualMachine if self.device and self.virtual_machine: raise ValidationError("A service cannot be associated with both a device and a virtual machine.") if not self.device and not self.virtual_machine: raise ValidationError("A service must be associated with either a device or a virtual machine.") def to_csv(self): return ( self.device.name if self.device else None, self.virtual_machine.name if self.virtual_machine else None, self.name, self.get_protocol_display(), self.port, self.description, )
class AbstractProposal(ConferenceRelated, EventInfo): submitter = BigForeignKey( to=settings.AUTH_USER_MODEL, verbose_name=_('submitter'), ) outline = models.TextField( verbose_name=_('outline'), blank=True, ) objective = EAWTextField( verbose_name=_('objective'), max_length=1000, ) supplementary = models.TextField( verbose_name=_('supplementary'), blank=True, default='', ) cancelled = models.BooleanField( verbose_name=_('cancelled'), default=False, db_index=True, ) additionalspeaker_set = GenericRelation( to=AdditionalSpeaker, content_type_field='proposal_type', object_id_field='proposal_id', ) objects = DefaultConferenceManager.from_queryset(ProposalQuerySet)() all_objects = models.Manager.from_queryset(ProposalQuerySet)() _must_fill_fields = [ 'abstract', 'objective', 'supplementary', 'detailed_description', 'outline', ] class Meta: abstract = True @property def speakers(self): yield PrimarySpeaker(proposal=self) additionals = self.additionalspeaker_set.filter(cancelled=False) for speaker in additionals.select_related('user'): yield speaker @property def speaker_count(self): return self.additionalspeaker_set.filter(cancelled=False).count() + 1 @property def must_fill_fields_count(self): return len(self._must_fill_fields) @property def finished_fields_count(self): count = sum(1 for f in self._must_fill_fields if getattr(self, f)) return count @property def finish_percentage(self): return 100 * self.finished_fields_count // self.must_fill_fields_count @property def unfinished_fields_count(self): return self.must_fill_fields_count - self.finished_fields_count
class UnidadeMedida(Nomeavel, TemChaveExterna): item = GenericRelation('ItemCadastro', related_query_name='unidade_medida') def __str__(self): return self.nome
class Bookmark(models.Model): name = models.CharField(max_length=60) tag = GenericRelation(FunkyTag, related_query_name='bookmark') def __str__(self): return self.name
class FriendRequest(models.Model): sender = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="sender") receiver = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="receiver") is_active = models.BooleanField(blank=True, null=False, default=True) timestamp = models.DateTimeField(auto_now_add=True) notifications = GenericRelation(Notification) def __str__(self): return self.sender.username def accept(self): # Accepting the friend request receiver_friend_list = FriendList.objects.get(user=self.receiver) if receiver_friend_list: content_type = ContentType.objects.get_for_model(self) # Updating notification for RECEIVER receiver_notification = Notification.objects.get( target=self.receiver, content_type=content_type, object_id=self.id) receiver_notification.is_active = False receiver_notification.redirect_url = f"{settings.BASE_URL}/account/{self.sender.pk}/" receiver_notification.statement = f"You accepted {self.sender.username}'s friend request." receiver_notification.timestamp = timezone.now() receiver_notification.save() receiver_friend_list.add_friend(self.sender) sender_friend_list = FriendList.objects.get(user=self.sender) if sender_friend_list: # Create notification for SENDER self.notifications.create( target=self.sender, from_user=self.receiver, redirect_url= f"{settings.BASE_URL}/account/{self.receiver.pk}/", statement= f"{self.receiver.username} accepted your friend request.", content_type=content_type, ) sender_friend_list.add_friend(self.receiver) self.is_active = False self.save() return receiver_notification def decline(self): # Declining the friend request self.is_active = False self.save() content_type = ContentType.objects.get_for_model(self) # Update notification for RECEIVER notification = Notification.objects.get(target=self.receiver, content_type=content_type, object_id=self.id) notification.is_active = False notification.redirect_url = f"{settings.BASE_URL}/account/{self.sender.pk}/" notification.statement = f"You declined {self.sender}'s friend request." notification.from_user = self.sender notification.timestamp = timezone.now() notification.save() # Create notification for SENDER self.notifications.create( target=self.sender, statement=f"{self.receiver.username} declined your friend request.", from_user=self.receiver, redirect_url=f"{settings.BASE_URL}/account/{self.receiver.pk}/", content_type=content_type, ) return notification def cancel(self): # Cancelling the request from the sender's perspective self.is_active = False self.save() content_type = ContentType.objects.get_for_model(self) # Create notification for SENDER self.notifications.create( target=self.sender, statement= f"You cancelled the friend request to {self.receiver.username}.", from_user=self.receiver, redirect_url=f"{settings.BASE_URL}/account/{self.receiver.pk}/", content_type=content_type, ) notification = Notification.objects.get(target=self.receiver, content_type=content_type, object_id=self.id) notification.statement = f"{self.sender.username} cancelled the friend request sent to you." # notification.timestamp = timezone.now() notification.read = False notification.save() @property def get_cname(self): """ For determining what kind of object is associated with a Notification """ return "FriendRequest"
class Experience(models.Model): # In fact, a brick's experience consists not only user reviews, # but also applications of the brick. # This class only contains user reviews # According to iGem's websites, a user review has no title. We can make this optional. title = models.CharField(max_length=MAX_LEN_FOR_THREAD_TITLE, blank=True, default='') # experience can be uploaded by users, so use Article to support markdown. content = models.OneToOneField(Article, null=True, on_delete=models.SET_NULL, default=None) author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='experience_set', null=True, default=None) # If the experience was uploaded by a user, the field author_name will be the username, # If the experience was fetched from the iGEM website, # the author_name should be set according to the data fetched. author_name = models.CharField(max_length=100, blank=True, default='') last_fetched = models.DateTimeField('last updated', null=True, default=None) # Automatically set the pub_time to now when the object is first created. # Also the pub_time can be set manually. pub_time = models.DateTimeField('publish time', auto_now_add=True) brick = models.ForeignKey('biobrick.BiobrickMeta', on_delete=models.CASCADE, null=True, default=None, related_name='experiences') votes = models.IntegerField(default=0) # add records for users mark down who has already voted for the post voted_users = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='experiences_voted') activities = GenericRelation('forum.Activity', 'target_id', 'target_type', related_query_name='experience') notices = GenericRelation('notices.Notice', 'target_id', 'target_type', related_query_name='experience') objects = ExperienceQuerySet.as_manager() def vote(self, user): from django.core.cache import cache from biohub.core.conf import settings as biohub_settings key = 'user_{}_vote'.format(user.id) if cache.get(key) is not None: raise Throttled() cache.set(key, 1, timeout=biohub_settings.THROTTLE['vote']) if self.author is not None and self.author.id == user.id: return False if not self.voted_users.filter(pk=user.id).exists(): with transaction.atomic(): self.votes += 1 self.voted_users.add(user) self.save(update_fields=['votes']) voted_experience_signal.send(sender=self.__class__, instance=self, user_voted=user, current_votes=self.votes) return True return False def unvote(self, user): if self.voted_users.filter(pk=user.id).exists(): with transaction.atomic(): self.voted_users.remove(user) self.votes -= 1 self.save(update_fields=['votes']) unvoted_experience_signal.send(sender=self.__class__, instance=self, user_unvoted=user) return True return False class Meta: ordering = ('-pub_time', 'id') def get_router_arguments(self): return 'experience', self.pk def __str__(self): return '%s' % self.title
class ActionPoint(TimeStampedModel): MODULE_CHOICES = Choices( ('t2f', _('Trip Management')), ('tpm', 'Third Party Monitoring'), ('audit', _('Auditor Portal')), ) STATUSES = Choices( ('open', _('Open')), ('completed', _('Completed')), ) STATUSES_DATES = { STATUSES.open: 'created', STATUSES.completed: 'date_of_completion' } KEY_EVENTS = Choices( ('status_update', _('Status Update')), ('reassign', _('Reassign')), ) author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='created_action_points', verbose_name=_('Author'), on_delete=models.CASCADE, ) assigned_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+', verbose_name=_('Assigned By'), on_delete=models.CASCADE, ) assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='assigned_action_points', verbose_name=_('Assigned To'), on_delete=models.CASCADE, ) status = FSMField(verbose_name=_('Status'), max_length=10, choices=STATUSES, default=STATUSES.open, protected=True) description = models.TextField(verbose_name=_('Description')) due_date = models.DateField(verbose_name=_('Due Date'), blank=True, null=True) high_priority = models.BooleanField(default=False, verbose_name=_('High Priority')) section = models.ForeignKey('reports.Sector', verbose_name=_('Section'), blank=True, null=True, on_delete=models.CASCADE, ) office = models.ForeignKey('users.Office', verbose_name=_('Office'), blank=True, null=True, on_delete=models.CASCADE, ) location = models.ForeignKey('locations.Location', verbose_name=_('Location'), blank=True, null=True, on_delete=models.CASCADE, ) partner = models.ForeignKey('partners.PartnerOrganization', verbose_name=_('Partner'), blank=True, null=True, on_delete=models.CASCADE, ) cp_output = models.ForeignKey('reports.Result', verbose_name=_('CP Output'), blank=True, null=True, on_delete=models.CASCADE, ) intervention = models.ForeignKey('partners.Intervention', verbose_name=_('PD/SSFA'), blank=True, null=True, on_delete=models.CASCADE, ) engagement = models.ForeignKey('audit.Engagement', verbose_name=_('Engagement'), blank=True, null=True, related_name='action_points', on_delete=models.CASCADE, ) tpm_activity = models.ForeignKey('tpm.TPMActivity', verbose_name=_('TPM Activity'), blank=True, null=True, related_name='action_points', on_delete=models.CASCADE, ) travel = models.ForeignKey('t2f.Travel', verbose_name=_('Travel'), blank=True, null=True, on_delete=models.CASCADE, ) date_of_completion = MonitorField(verbose_name=_('Date Action Point Completed'), null=True, blank=True, default=None, monitor='status', when=[STATUSES.completed]) comments = GenericRelation('django_comments.Comment', object_id_field='object_pk') history = GenericRelation('snapshot.Activity', object_id_field='target_object_id', content_type_field='target_content_type') tracker = FieldTracker(fields=['assigned_to']) class Meta: ordering = ('id', ) verbose_name = _('Action Point') verbose_name_plural = _('Action Points') @property def engagement_subclass(self): return self.engagement.get_subclass() if self.engagement else None @property def related_object(self): return self.engagement_subclass or self.tpm_activity or self.travel @property def related_module(self): if self.engagement: return self.MODULE_CHOICES.audit if self.tpm_activity: return self.MODULE_CHOICES.tpm if self.travel: return self.MODULE_CHOICES.t2f return None @property def reference_number(self): return '{}/{}/{}/APD'.format( connection.tenant.country_short_code or '', self.created.year, self.id, ) def get_object_url(self, **kwargs): return build_frontend_url('apd', 'action-points', 'detail', self.id, **kwargs) @property def status_date(self): return getattr(self, self.STATUSES_DATES[self.status]) def __str__(self): return self.reference_number def get_meaningful_history(self): return self.history.filter( models.Q(action=Activity.CREATE) | models.Q(models.Q(action=Activity.UPDATE), ~models.Q(change={})) ) def snapshot_additional_data(self, diff): key_events = [] if 'status' in diff: key_events.append(self.KEY_EVENTS.status_update) if 'assigned_to' in diff: key_events.append(self.KEY_EVENTS.reassign) return {'key_events': key_events} @classmethod def get_snapshot_action_display(cls, activity): key_events = activity.data.get('key_events') if key_events: if cls.KEY_EVENTS.status_update in key_events: return _('Changed status to {}').format(cls.STATUSES[activity.change['status']['after']]) elif cls.KEY_EVENTS.reassign in key_events: return _('Reassigned to {}').format( get_user_model().objects.get(pk=activity.change['assigned_to']['after']).get_full_name() ) return activity.get_action_display() def get_mail_context(self, user=None, include_token=False): return { 'person_responsible': self.assigned_to.get_full_name(), 'assigned_by': self.assigned_by.get_full_name(), 'reference_number': self.reference_number, 'partner': self.partner.name if self.partner else '', 'description': self.description, 'due_date': self.due_date.strftime('%d %b %Y') if self.due_date else '', 'object_url': self.get_object_url(user=user, include_token=include_token), } def send_email(self, recipient, template_name, additional_context=None): context = { 'environment': get_environment(), 'action_point': self.get_mail_context(user=recipient), 'recipient': recipient.get_full_name(), } context.update(additional_context or {}) notification = Notification.objects.create( sender=self, recipients=[recipient.email], template_name=template_name, template_data=context ) notification.send_notification() def _do_complete(self): self.send_email(self.assigned_by, 'action_points/action_point/completed') @transition(status, source=STATUSES.open, target=STATUSES.completed, permission=has_action_permission(action='complete'), conditions=[ ActionPointCompleteActionsTakenCheck.as_condition() ]) def complete(self): self._do_complete()
class Task(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models.Model): user_story = models.ForeignKey("userstories.UserStory", null=True, blank=True, related_name="tasks", verbose_name=_("user story")) ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None, verbose_name=_("ref")) owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None, related_name="owned_tasks", verbose_name=_("owner")) status = models.ForeignKey("projects.TaskStatus", null=True, blank=True, related_name="tasks", verbose_name=_("status")) project = models.ForeignKey("projects.Project", null=False, blank=False, related_name="tasks", verbose_name=_("project")) milestone = models.ForeignKey("milestones.Milestone", null=True, blank=True, on_delete=models.SET_NULL, default=None, related_name="tasks", verbose_name=_("milestone")) created_date = models.DateTimeField(null=False, blank=False, verbose_name=_("created date"), default=timezone.now) modified_date = models.DateTimeField(null=False, blank=False, verbose_name=_("modified date")) finished_date = models.DateTimeField(null=True, blank=True, verbose_name=_("finished date")) subject = models.TextField(null=False, blank=False, verbose_name=_("subject")) us_order = models.IntegerField(null=False, blank=False, default=1, verbose_name=_("us order")) taskboard_order = models.IntegerField(null=False, blank=False, default=1, verbose_name=_("taskboard order")) description = models.TextField(null=False, blank=True, verbose_name=_("description")) assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, default=None, related_name="tasks_assigned_to_me", verbose_name=_("assigned to")) attachments = GenericRelation("attachments.Attachment") is_iocaine = models.BooleanField(default=False, null=False, blank=True, verbose_name=_("is iocaine")) external_reference = TextArrayField(default=None, verbose_name=_("external reference")) _importing = None class Meta: verbose_name = "task" verbose_name_plural = "tasks" ordering = ["project", "created_date", "ref"] # unique_together = ("ref", "project") permissions = (("view_task", "Can view task"), ) def save(self, *args, **kwargs): if not self._importing or not self.modified_date: self.modified_date = timezone.now() if not self.status: self.status = self.project.default_task_status return super().save(*args, **kwargs) def __str__(self): return "({1}) {0}".format(self.ref, self.subject)
def enable_voting_on(cls, manager_name='objects', votes_name='votes', upvotes_name='total_upvotes', downvotes_name='total_downvotes', total_name='vote_total', add_vote_name='add_vote', remove_vote_name='remove_vote', base_manager=None): from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericRelation from secretballot.models import Vote VOTE_TABLE = Vote._meta.db_table def add_vote(self, token, vote): voteobj, created = getattr(self, votes_name).get_or_create(token=token, defaults={ 'vote': vote, 'content_object': self }) if not created: voteobj.vote = vote voteobj.save() def remove_vote(self, token): getattr(self, votes_name).filter(token=token).delete() # gets added to the class as a property, not under this name def get_total(self): return getattr(self, upvotes_name) - getattr(self, downvotes_name) if base_manager is None: if hasattr(cls, manager_name): base_manager = getattr(cls, manager_name).__class__ else: base_manager = Manager class VotableManager(base_manager): use_for_related_fields = True def get_queryset(self): db_table = self.model._meta.db_table pk_name = self.model._meta.pk.attname content_type = ContentType.objects.get_for_model(self.model).id downvote_query = '(SELECT COUNT(*) from %s WHERE vote=-1 AND object_id=%s.%s AND content_type_id=%s)' % (VOTE_TABLE, db_table, pk_name, content_type) upvote_query = '(SELECT COUNT(*) from %s WHERE vote=1 AND object_id=%s.%s AND content_type_id=%s)' % (VOTE_TABLE, db_table, pk_name, content_type) return super(VotableManager, self).get_queryset().extra( select={upvotes_name: upvote_query, downvotes_name: downvote_query}) def from_token(self, token): db_table = self.model._meta.db_table pk_name = self.model._meta.pk.attname content_type = ContentType.objects.get_for_model(self.model).id query = '(SELECT vote from %s WHERE token=%%s AND object_id=%s.%s AND content_type_id=%s)' % (VOTE_TABLE, db_table, pk_name, content_type) return self.get_queryset().extra(select={'user_vote': query}, select_params=(token,)) def from_request(self, request): if not hasattr(request, 'secretballot_token'): raise ImproperlyConfigured('To use secretballot a SecretBallotMiddleware must ' 'be installed. (see secretballot/middleware.py)') return self.from_token(request.secretballot_token) cls.add_to_class('_default_manager', VotableManager()) cls.add_to_class(manager_name, VotableManager()) cls.add_to_class(votes_name, GenericRelation(Vote)) cls.add_to_class(total_name, property(get_total)) cls.add_to_class(add_vote_name, add_vote) cls.add_to_class(remove_vote_name, remove_vote) setattr(cls, '_secretballot_enabled', True)
class Adjudicator(Person): institution = models.ForeignKey(Institution, models.SET_NULL, blank=True, null=True, verbose_name=_("institution")) # cascade to avoid unattached adjudicator pollution when deleting tournaments tournament = models.ForeignKey('tournaments.Tournament', models.CASCADE, blank=True, null=True, verbose_name=_("tournament"), help_text=_("Adjudicators not assigned to any tournament can be shared between tournaments")) base_score = models.FloatField(default=0, verbose_name=_("base score")) institution_conflicts = models.ManyToManyField('Institution', through='adjallocation.AdjudicatorInstitutionConflict', related_name='adj_inst_conflicts', verbose_name=_("institution conflicts")) team_conflicts = models.ManyToManyField('Team', through='adjallocation.AdjudicatorTeamConflict', related_name='adj_team_conflicts', verbose_name=_("team conflicts")) adjudicator_conflicts = models.ManyToManyField('Adjudicator', through='adjallocation.AdjudicatorAdjudicatorConflict', related_name='adj_adj_conflicts', verbose_name=_("adjudicator conflicts")) trainee = models.BooleanField(default=False, verbose_name=_("always trainee"), help_text=_("If checked, this adjudicator will never be auto-allocated a voting position, regardless of their score")) breaking = models.BooleanField(default=False, verbose_name=_("breaking")) independent = models.BooleanField(default=False, blank=True, verbose_name=_("independent")) adj_core = models.BooleanField(default=False, blank=True, verbose_name=_("adjudication core")) round_availabilities = GenericRelation('availability.RoundAvailability') venue_constraints = GenericRelation('venues.VenueConstraint', related_query_name='adjudicator', content_type_field='subject_content_type', object_id_field='subject_id') objects = AdjudicatorManager() class Meta: verbose_name = _("adjudicator") verbose_name_plural = _("adjudicators") def __str__(self): if self.institution is None: return self.name else: return "%s (%s)" % (self.name, self.institution.code) @property def region(self): return self.institution.region if self.institution else None def weighted_score(self, feedback_weight): feedback_score = self._feedback_score() if feedback_score is None: feedback_score = 0 feedback_weight = 0 return self.base_score * (1 - feedback_weight) + (feedback_weight * feedback_score) @cached_property def score(self): warn("Adjudicator.score is inefficient; consider using Adjudicator.weighted_score() instead.", stacklevel=2) if self.tournament: weight = self.tournament.current_round.feedback_weight else: weight = 1 # For shared ajudicators return self.weighted_score(weight) def _feedback_score(self): try: return self._feedback_score_cache except AttributeError: from adjallocation.models import DebateAdjudicator self._feedback_score_cache = self.adjudicatorfeedback_set.filter(confirmed=True, ignored=False).exclude( source_adjudicator__type=DebateAdjudicator.TYPE_TRAINEE).aggregate( avg=models.Avg('score'))['avg'] return self._feedback_score_cache @property def feedback_score(self): return self._feedback_score() or None def get_feedback(self): return self.adjudicatorfeedback_set.all()
class Team(models.Model): reference = models.CharField(blank=True, max_length=150, verbose_name=_("full name/suffix"), help_text=_("Do not include institution name (see \"uses institutional prefix\" below)")) short_reference = models.CharField(blank=True, max_length=35, verbose_name=_("short name/suffix"), help_text=_("The name shown in the draw. Do not include institution name (see \"uses institutional prefix\" below)")) code_name = models.CharField(blank=True, max_length=150, verbose_name=_("code name"), help_text=_("Name used to obscure institutional identity on public-facing pages")) short_name = models.CharField(editable=False, max_length=20+1+35, # Max institution code + space + short_reference max verbose_name=_("short name"), help_text=_("The name shown in the draw, including institution name. (This is autogenerated.)")) long_name = models.CharField(editable=False, max_length=100+1+150, # Max institution name + space + reference max verbose_name=_("long name"), help_text=_("The full name of the team, including institution name. (This is autogenerated.)")) institution = models.ForeignKey(Institution, models.SET_NULL, blank=True, null=True, verbose_name=_("institution")) tournament = models.ForeignKey('tournaments.Tournament', models.CASCADE, verbose_name=_("tournament")) use_institution_prefix = models.BooleanField(default=False, verbose_name=_("Uses institutional prefix"), help_text=_("If ticked, a team called \"1\" from Victoria will be shown as \"Victoria 1\"")) break_categories = models.ManyToManyField('breakqual.BreakCategory', blank=True, verbose_name=_("break categories")) institution_conflicts = models.ManyToManyField('Institution', through='adjallocation.TeamInstitutionConflict', related_name='team_inst_conflicts', verbose_name=_("institution conflicts")) round_availabilities = GenericRelation('availability.RoundAvailability') venue_constraints = GenericRelation('venues.VenueConstraint', related_query_name='team', content_type_field='subject_content_type', object_id_field='subject_id') TYPE_NONE = 'N' TYPE_SWING = 'S' TYPE_COMPOSITE = 'C' TYPE_BYE = 'B' TYPE_CHOICES = ( (TYPE_NONE, _("none")), (TYPE_SWING, _("swing")), (TYPE_COMPOSITE, _("composite")), (TYPE_BYE, _("bye")), ) type = models.CharField(max_length=1, choices=TYPE_CHOICES, default=TYPE_NONE, verbose_name=_("type")) emoji = models.CharField(max_length=2, default=None, choices=EMOJI_FIELD_CHOICES, blank=True, null=True, # uses null=True to allow multiple teams to have no emoji verbose_name=_("emoji")) class Meta: unique_together = [ # Enforce for blank references also - two teams from the same # institution can't both be unlabelled. However, Django won't # enforce this for null institutions. ('reference', 'institution', 'tournament'), # Not enforced for blank emoji (null=True is set on emoji) ('emoji', 'tournament'), ] ordering = ['tournament', 'institution', 'short_reference'] index_together = ['tournament', 'institution', 'short_reference'] verbose_name = _("team") verbose_name_plural = _("teams") objects = TeamManager() def __str__(self): return "[{}] {}".format(self.tournament.slug, self.short_name) def _construct_short_name(self): institution = self.institution reference = self.short_reference or self.reference if self.use_institution_prefix and institution is not None: short_name = institution.code if reference: short_name += " " + str(reference)[:35] return short_name else: return str(reference)[:20+1+35] def _construct_long_name(self): institution = self.institution if self.use_institution_prefix and institution is not None: long_name = institution.name if self.reference: long_name += " " + self.reference return long_name else: return self.reference @property def region(self): return self.institution.region if self.institution else None def break_rank_for_category(self, category): from breakqual.models import BreakingTeam try: bt = BreakingTeam.objects.get(break_category=category, team=self) except BreakingTeam.DoesNotExist: return None return bt.break_rank def get_debates(self, before_round): dts = self.debateteam_set.select_related('debate').order_by( 'debate__round__seq') if before_round is not None: dts = dts.filter(debate__round__seq__lt=before_round) return [dt.debate for dt in dts] @property def debates(self): return self.get_debates(None) @property def wins_count(self): """Callers using this property for many teams should prefetch them using `populate_win_counts()` in the `participants.prefetch` module.""" try: return self._wins_count except AttributeError: from results.models import TeamScore self._wins_count = TeamScore.objects.filter(ballot_submission__confirmed=True, debate_team__team=self, win=True).count() return self._wins_count @property def points_count(self): """Callers using this property for many teams should prefetch them using `populate_win_counts()` in the `participants.prefetch` module. (That's not a typo -- that function populates both `_wins_count` and `_points`.)""" try: return self._points except AttributeError: from results.models import TeamScore self._points = TeamScore.objects.filter(ballot_submission__confirmed=True, debate_team__team=self).aggregate(Sum('points'))['points__sum'] return self._points @cached_property def speakers(self): return self.speaker_set.all() def seen(self, other, before_round=None): queryset = self.debateteam_set.filter(debate__debateteam__team=other) if before_round: queryset = queryset.filter(debate__round__seq__lt=before_round) return queryset.count() def same_institution(self, other): """Returns True if this team and `other` are from the same institution. Always returns False if this team has no institution.""" return self.institution_id is not None and self.institution_id == other.institution_id def prev_debate(self, round_seq): from draw.models import DebateTeam try: return DebateTeam.objects.filter( debate__round__seq__lt=round_seq, team=self).order_by('-debate__round__seq')[0].debate except IndexError: return None def clean(self): # Require reference and short_reference if use_institution_prefix is False errors = {} if self.use_institution_prefix and self.institution is None: errors['institution'] = _("Teams must have an institution if they are using the institutional prefix.") if not self.use_institution_prefix and not self.reference: errors['reference'] = _("Teams must have a full name if they don't use the institutional prefix.") if not self.use_institution_prefix and not self.short_reference: errors['short_reference'] = _("Teams must have a short name if they don't use the institutional prefix.") if errors: raise ValidationError(errors) def save(self, *args, **kwargs): # Override the short and long names before saving self.short_name = self._construct_short_name() self.long_name = self._construct_long_name() super().save(*args, **kwargs)
class Creator(GcdData): class Meta: app_label = 'gcd' ordering = ('sort_name', 'created',) verbose_name_plural = 'Creators' objects = CreatorManager() gcd_official_name = models.CharField(max_length=255, db_index=True) sort_name = models.CharField(max_length=255, db_index=True, default='') disambiguation = models.CharField(max_length=255, default='', db_index=True) birth_date = models.ForeignKey(Date, on_delete=models.CASCADE, related_name='+', null=True) death_date = models.ForeignKey(Date, on_delete=models.CASCADE, related_name='+', null=True) whos_who = models.URLField(null=True) birth_country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='birth_country', null=True) birth_country_uncertain = models.BooleanField(default=False) birth_province = models.CharField(max_length=50) birth_province_uncertain = models.BooleanField(default=False) birth_city = models.CharField(max_length=200) birth_city_uncertain = models.BooleanField(default=False) death_country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='death_country', null=True) death_country_uncertain = models.BooleanField(default=False) death_province = models.CharField(max_length=50) death_province_uncertain = models.BooleanField(default=False) death_city = models.CharField(max_length=200) death_city_uncertain = models.BooleanField(default=False) portrait = GenericRelation(Image) awards = GenericRelation(ReceivedAward) bio = models.TextField() sample_scan = GenericRelation(Image) notes = models.TextField() data_source = models.ManyToManyField(DataSource) def _portrait(self): content_type = ContentType.objects.get_for_model(self) img = Image.objects.filter(object_id=self.id, deleted=False, content_type=content_type, type__id=4) if img: return img.get() else: return None portrait = property(_portrait) def _samplescan(self): content_type = ContentType.objects.get_for_model(self) img = Image.objects.filter(object_id=self.id, deleted=False, content_type=content_type, type__id=5) if img: return img.get() else: return None samplescan = property(_samplescan) def full_name(self): return str(self) def display_birthday(self): return _display_day(self.birth_date) def display_birthplace(self): return _display_place(self, 'birth') def display_deathday(self): return _display_day(self.death_date) def display_deathplace(self): return _display_place(self, 'death') def has_death_info(self): if str(self.death_date) != '': return True else: return False def has_dependents(self): if self.creator_names.filter(storycredit__deleted=False).exists(): return True if self.art_influence_revisions.active_set().exists(): return True # TODO how to handle GenericRelation for ReceivedAward # if self.award_revisions.filter.active_set().count(): # return True if self.degree_revisions.active_set().exists(): return True if self.membership_revisions.active_set().exists(): return True if self.non_comic_work_revisions.active_set().exists(): return True if self.school_revisions.active_set().exists(): return True if self.active_relations().exists(): return True if self.active_influenced_creators().exists(): return True return False def active_names(self): return self.creator_names.exclude(deleted=True) def active_art_influences(self): return self.art_influences.exclude(deleted=True)\ .order_by('influence_link__sort_name', 'influence_name') def active_influenced_creators(self): return self.influenced_creators.exclude(deleted=True)\ .order_by('creator__sort_name') def active_awards(self): return self.awards.exclude(deleted=True) def active_awards_for_issues(self): from .issue import Issue issues = Issue.objects.filter(story__credits__creator__creator=self, awards__isnull=False).distinct() content_type = ContentType.objects.get(model='Issue') awards = ReceivedAward.objects.filter(content_type=content_type, object_id__in=issues) return awards def active_awards_for_stories(self): from .story import Story stories = Story.objects.filter(credits__creator__creator=self, awards__isnull=False).distinct() content_type = ContentType.objects.get(model='Story') awards = ReceivedAward.objects.filter(content_type=content_type, object_id__in=stories) return awards def active_degrees(self): return self.degree_set.exclude(deleted=True) def active_memberships(self): return self.membership_set.exclude(deleted=True) def active_non_comic_works(self): return self.non_comic_work_set.exclude(deleted=True) def active_relations(self): return self.from_related_creator.exclude(deleted=True) | \ self.to_related_creator.exclude(deleted=True) def active_schools(self): return self.school_set.exclude(deleted=True) def active_signatures(self): return self.signatures.exclude(deleted=True) _update_stats = True def stat_counts(self): """ Returns all count values relevant to this creator. """ if self.deleted: return {} return {'creators': 1} def get_absolute_url(self): return urlresolvers.reverse( 'show_creator', kwargs={'creator_id': self.id}) def __str__(self): if self.birth_date.year: year = '(b. %s)' % self.birth_date.year else: year = '' return '%s %s' % (str(self.gcd_official_name), year)
class Directory(TendenciBaseModel): guid = models.CharField(max_length=40) slug = SlugField(_('URL Path'), unique=True) timezone = TimeZoneField(_('Time Zone')) headline = models.CharField(_('Name'), max_length=200, blank=True) summary = models.TextField(blank=True) body = tinymce_models.HTMLField(_('Description')) source = models.CharField(max_length=300, blank=True) # logo = models.FileField(max_length=260, upload_to=file_directory, # help_text=_('Company logo. Only jpg, gif, or png images.'), # blank=True) logo_file = models.ForeignKey(File, null=True) first_name = models.CharField(_('First Name'), max_length=100, blank=True) last_name = models.CharField(_('Last Name'), max_length=100, blank=True) address = models.CharField(_('Address'), max_length=100, blank=True) address2 = models.CharField(_('Address 2'), max_length=100, blank=True) city = models.CharField(_('City'), max_length=50, blank=True) state = models.CharField(_('State'), max_length=50, blank=True) zip_code = models.CharField(_('Zip Code'), max_length=50, blank=True) country = models.CharField(_('Country'), max_length=50, blank=True) phone = models.CharField(max_length=50, blank=True) phone2 = models.CharField(_('Phone 2'), max_length=50, blank=True) fax = models.CharField(_('Fax'), max_length=50, blank=True) email = models.CharField(_('Email'), max_length=120, blank=True) email2 = models.CharField(_('Email 2'), max_length=120, blank=True) website = models.CharField(max_length=300, blank=True) renewal_notice_sent = models.BooleanField(default=False) list_type = models.CharField(_('List Type'), max_length=50, blank=True) requested_duration = models.IntegerField(_('Requested Duration'), default=0) pricing = models.ForeignKey('DirectoryPricing', null=True) activation_dt = models.DateTimeField(_('Activation Date/Time'), null=True, blank=True) expiration_dt = models.DateTimeField(_('Expiration Date/Time'), null=True, blank=True) invoice = models.ForeignKey(Invoice, blank=True, null=True) payment_method = models.CharField(_('Payment Method'), max_length=50, blank=True) syndicate = models.BooleanField(_('Include in RSS feed'), default=True) design_notes = models.TextField(_('Design Notes'), blank=True) admin_notes = models.TextField(_('Admin Notes'), blank=True) tags = TagField(blank=True) # for podcast feeds enclosure_url = models.CharField(_('Enclosure URL'), max_length=500, blank=True) enclosure_type = models.CharField(_('Enclosure Type'), max_length=120, blank=True) enclosure_length = models.IntegerField(_('Enclosure Length'), default=0) # html-meta tags meta = models.OneToOneField(MetaTags, null=True) cat = models.ForeignKey(Category, verbose_name=_("Category"), related_name="directory_cat", null=True, on_delete=models.SET_NULL) sub_cat = models.ForeignKey(Category, verbose_name=_("Sub Category"), related_name="directory_subcat", null=True, on_delete=models.SET_NULL) # legacy categories needed for data migration categories = GenericRelation(CategoryItem, object_id_field="object_id", content_type_field="content_type") perms = GenericRelation(ObjectPermission, object_id_field="object_id", content_type_field="content_type") objects = DirectoryManager() class Meta: permissions = (("view_directory",_("Can view directory")),) verbose_name = _("Directory") verbose_name_plural = _("Directories") app_label = 'directories' def get_meta(self, name): """ This method is standard across all models that are related to the Meta model. Used to generate dynamic methods coupled to this instance. """ return DirectoryMeta().get_meta(self, name) @models.permalink def get_absolute_url(self): return ("directory", [self.slug]) @models.permalink def get_renew_url(self): return ("directory.renew", [self.id]) def __unicode__(self): return self.headline def save(self, *args, **kwargs): if not self.id: self.guid = str(uuid.uuid1()) super(Directory, self).save(*args, **kwargs) if self.logo: if self.is_public(): set_s3_file_permission(self.logo.name, public=True) else: set_s3_file_permission(self.logo.name, public=False) def is_public(self): return all([self.allow_anonymous_view, self.status, self.status_detail in ['active']]) @property def logo(self): """ This represents the logo FileField Originally this was a FileField, but later we added the attribute logo_file to leverage the File model. We then replaced the logo property with this convience method for backwards compatibility. """ if self.logo_file: return self.logo_file.file def get_logo_url(self): if not self.logo_file: return u'' return reverse('file', args=[self.logo_file.pk]) # Called by payments_pop_by_invoice_user in Payment model. def get_payment_description(self, inv): """ The description will be sent to payment gateway and displayed on invoice. If not supplied, the default description will be generated. """ return 'Tendenci Invoice %d for Directory: %s (%d).' % ( inv.id, self.headline, inv.object_id, ) def make_acct_entries(self, user, inv, amount, **kwargs): """ Make the accounting entries for the directory sale """ from tendenci.apps.accountings.models import Acct, AcctEntry, AcctTran from tendenci.apps.accountings.utils import make_acct_entries_initial, make_acct_entries_closing ae = AcctEntry.objects.create_acct_entry(user, 'invoice', inv.id) if not inv.is_tendered: make_acct_entries_initial(user, ae, amount) else: # payment has now been received make_acct_entries_closing(user, ae, amount) # #CREDIT directory SALES acct_number = self.get_acct_number() acct = Acct.objects.get(account_number=acct_number) AcctTran.objects.create_acct_tran(user, ae, acct, amount*(-1)) def get_acct_number(self, discount=False): if discount: return 464400 else: return 404400 def auto_update_paid_object(self, request, payment): """ Update the object after online payment is received. """ if not request.user.profile.is_superuser: self.status_detail = 'paid - pending approval' self.save() def age(self): return datetime.now() - self.create_dt @property def category_set(self): items = {} for cat in self.categories.select_related('category__name', 'parent__name'): if cat.category: items["category"] = cat.category elif cat.parent: items["sub_category"] = cat.parent return items def renew_window(self): days = get_setting('module', 'directories', 'renewaldays') days = int(days) if self.expiration_dt and datetime.now() + timedelta(days) > self.expiration_dt: return True else: return False
class LotMixin(models.Model): BOROUGH_CHOICES = ( ('Bronx', 'Bronx'), ('Brooklyn', 'Brooklyn'), ('Manhattan', 'Manhattan'), ('Queens', 'Queens'), ('Staten Island', 'Staten Island'), ) accessible = models.BooleanField(default=True) bbl = models.CharField(max_length=10, blank=True, null=True) block = models.IntegerField(blank=True, null=True) borough = models.CharField(max_length=25, choices=BOROUGH_CHOICES) gutterspace = models.BooleanField(default=False) lot_number = models.IntegerField(blank=True, null=True) organizers = GenericRelation(Organizer) organizing = models.BooleanField(default=False) parcel = models.ForeignKey( 'parcels.Parcel', blank=True, null=True, ) commons_content_type = models.ForeignKey(ContentType, null=True) commons_object_id = models.PositiveIntegerField(null=True) commons_content_object = GenericForeignKey('commons_content_type', 'commons_object_id') commons_type = models.CharField(max_length=25, choices=COMMONS_TYPES) priority = models.BooleanField( default=False, verbose_name=_('Development pending'), ) development_pending_explanation = tinymce_models.HTMLField( blank=True, null=True, help_text=_( 'If development is pending, let visitors to the site know why.'), ) files = GenericRelation('files.File') groundtruth_records = GenericRelation('groundtruth.GroundtruthRecord') notes = GenericRelation('notes.Note') photos = GenericRelation('photos.Photo') steward_notifications = GenericRelation('steward.StewardNotification') steward_projects = GenericRelation('steward.StewardProject') owner_opt_in = models.BooleanField(default=False) def _get_display_name(self): if self.name: return self.name if self.bbl_is_fake: if self.address_line1: return self.address_line1 return '%s, unmapped lot #%d' % (self.borough, self.lot_number) try: return '%s block %d, lot %d' % (self.borough, self.block, self.lot_number) except TypeError: try: blocks = list(set([l.block for l in self.lots])) block_strs = [] for block in sorted(blocks): block_lots = [l for l in self.lots if l.block == block] block_strs.append('block %d, %s' % (block, '%s %s' % ( 'lot' if len(block_lots) == 1 else 'lots', ', '.join( sorted([str(l.lot_number) for l in block_lots])), ))) return '%s %s' % (self.borough, '; '.join(block_strs)) except TypeError: return self.address_line1 display_name = property(_get_display_name) @classmethod def get_filter(cls): from .filters import LotFilter return LotFilter def calculate_polygon_area(self): try: return self.polygon.transform(2263, clone=True).area except Exception: return None def _area(self): if not self.polygon_area: try: # Try to get area from parcel's PLUTO data self.polygon_area = self.parcel.lotarea except Exception: self.polygon_area = self.calculate_polygon_area() self.save() return self.polygon_area area = property(_area) def _area_acres(self): try: area = self.area * (ureg.feet**2) return area.to(ureg.acre).magnitude except (ValueError, TypeError): return None area_acres = property(_area_acres) def _bbl_is_fake(self): return self.bbl and self.bbl.startswith('6') bbl_is_fake = property(_bbl_is_fake) def _owners(self): owners = [ self.owner, ] + [l.owner for l in self.lots] return [o for o in set(owners) if o] owners = property(_owners) def _owner_contacts(self): contacts = [ self.owner_contact, ] contacts += [l.owner_contact for l in self.lots] for l in self.lots: if not l.owner: continue if l.owner.default_contact: contacts.append(l.owner.default_contact) elif l.owner.ownercontact_set.count() == 1: contacts.append(l.owner.ownercontact_set.all()[0]) # Dedupe while keeping correct order return sorted(list(set(filter(None, contacts))), key=contacts.index) owner_contacts = property(_owner_contacts) def get_owner_contact(self): if self.owner_contact: return self.owner_contact if len(self.owner_contacts) == 1: return self.owner_contacts[0] def _bbox(self): try: return list(self.polygon.extent) except Exception: return list(self.centroid.buffer(.0005).extent) bbox = property(_bbox) def _get_lots(self): try: return self.lotgroup.lot_set.all().order_by('block', 'lot_number') except Exception: return [ self, ] lots = property(_get_lots) def get_new_lotgroup_kwargs(self): kwargs = super(LotMixin, self).get_new_lotgroup_kwargs() kwargs.update({ 'borough': self.borough, 'commons_type': self.commons_type, 'known_use': self.known_use, 'known_use_certainty': self.known_use_certainty, 'known_use_locked': self.known_use_locked, 'steward_inclusion_opt_in': self.steward_inclusion_opt_in, 'owner': self.owner, 'owner_opt_in': self.owner_opt_in, }) return kwargs def reassign_objects(self, new_lot, **kwargs): """Reassign related objects (eg, notes or organizers) to the new lot""" self.files.update(**kwargs) self.notes.update(**kwargs) self.organizers.update(**kwargs) self.photos.update(**kwargs) self.steward_projects.update(**kwargs) # Handle things with MonitorEntrys (moderated) monitor_objs = (list(self.groundtruth_records.all()) + list(self.steward_notifications.all())) for obj in monitor_objs: obj.content_object = new_lot obj.save() def __unicode__(self): if self.display_name: return self.display_name return u'%d' % self.pk class Meta: abstract = True
class Member(TimeStampedModel): id = models.UUIDField( primary_key=True, default=uuid.uuid4, editable=False, ) STATUS = Choices( ( -10, 'inactive', 'Inactive', ), ( 0, 'new', 'New', ), ( 10, 'active', 'Active', ), ) status = FSMIntegerField( help_text= """DO NOT CHANGE MANUALLY unless correcting a mistake. Use the buttons to change state.""", choices=STATUS, default=STATUS.new, ) # MEM_STATUS = Choices( # (10, 'active', 'Active',), # (20, 'active_internal', 'Active Internal',), # (30, 'active_licensed', 'Active Licensed',), # (40, 'cancelled', 'Cancelled',), # (50, 'closed', 'Closed',), # (60, 'closed_merged', 'Closed Merged',), # (70, 'closed_revoked', 'Closed Revoked',), # (80, 'closed_voluntary', 'Closed Voluntary',), # (90, 'expelled', 'Expelled',), # (100, 'expired', 'Expired',), # (105, 'expired_licensed', 'Expired Licensed',), # (110, 'lapsed', 'Lapsed',), # (120, 'not_approved', 'Not Approved',), # (130, 'pending', 'Pending',), # (140, 'pending_voluntary', 'Pending Voluntary',), # (150, 'suspended', 'Suspended',), # (160, 'suspended_membership', 'Suspended Membership',), # (170, 'awaiting_payment', 'Awaiting Payment',), # ) # mem_status = models.IntegerField( # choices=MEM_STATUS, # null=True, # blank=True, # ) # SUB_STATUS = Choices( # (10, 'active', 'Active',), # (20, 'expired', 'Expired',), # (30, 'pending', 'Pending',), # (40, 'lapsedRenew', 'Lapsed',), # (50, 'cancelled', 'Cancelled',), # (60, 'swapped', 'Swapped',), # ) # sub_status = models.IntegerField( # choices=SUB_STATUS, # null=True, # blank=True, # ) # MEM_CODE = Choices( # (10, 'RG', 'RG Regular'), # (20, 'R5', 'R5 Regular 50 Year'), # (30, 'SN', 'SN Senior'), # (40, 'S5', 'S5 Senior 50 Year'), # (50, 'SL', 'SL Senior Legacy'), # (60, 'Y1', 'Y1 Youth Initial'), # (70, 'Y2', 'Y2 Youth Subsequent'), # (80, 'LF', 'LF Lifetime Regular'), # (90, 'L5', 'L5 Lifetime 50 Year'), # (100, 'LY', 'LY Lifetime Youth'), # (110, 'LS', 'LS Lifetime Senior'), # (120, 'AS', 'AS Associate'), # ) # mem_code = models.IntegerField( # choices=MEM_CODE, # null=True, # blank=True, # ) # inactive_date = models.DateField( # null=True, # blank=True, # ) # INACTIVE_REASON = Choices( # (1, 'Non_renewal', 'Non-Renewal'), # (2, 'Renewed', 'Renewed'), # (3, 'NotCancelled', 'Not Cancelled'), # (4, 'Non_Payment', 'Non-Payment'), # (5, 'Expired', 'Expired'), # (6, 'Deceased', 'Deceased'), # (7, 'changedOption', 'changedOption'), # (8, 'Other', 'Other'), # (9, 'cancelled', 'cancelled'), # (10, 'Transferred', 'Transferred'), # (11, 'swappedChapter', 'swappedChapter'), # (12, 'swapped', 'swapped'), # ) # inactive_reason = models.IntegerField( # choices=INACTIVE_REASON, # null=True, # blank=True, # ) PART = Choices( # (-1, 'director', 'Director'), (1, 'tenor', 'Tenor'), (2, 'lead', 'Lead'), (3, 'baritone', 'Baritone'), (4, 'bass', 'Bass'), ) part = models.IntegerField( choices=PART, null=True, blank=True, ) start_date = models.DateField( null=True, blank=True, ) end_date = models.DateField( null=True, blank=True, ) mc_pk = models.CharField( null=True, blank=True, max_length=36, unique=True, db_index=True, ) # Properties @cached_property def is_mc(self): return bool(self.mc_pk) # FKs group = models.ForeignKey( 'Group', related_name='members', on_delete=models.CASCADE, ) person = models.ForeignKey( 'Person', related_name='members', on_delete=models.CASCADE, ) # Relations statelogs = GenericRelation( StateLog, related_query_name='members', ) # Internals objects = MemberManager() class Meta: unique_together = (( 'group', 'person', ), ) class JSONAPIMeta: resource_name = "member" def __str__(self): return str(self.id) def clean(self): if all([ self.status == self.STATUS.active, self.person.status == self.person.STATUS.inactive, ]): raise ValidationError({ 'status': 'Can not be active when person is inactive', }) if self.end_date: if all([ self.status == self.STATUS.active, self.end_date < now().date(), ]): raise ValidationError({ 'status': 'Can not be active with a passed end date', }) # Permissions @staticmethod @allow_staff_or_superuser @authenticated_users def has_read_permission(request): return True @allow_staff_or_superuser @authenticated_users def has_object_read_permission(self, request): return True @staticmethod @allow_staff_or_superuser @authenticated_users def has_write_permission(request): return any([ request.user.is_group_manager, ]) @allow_staff_or_superuser @authenticated_users def has_object_write_permission(self, request): return any([ all([ self.group.officers.filter( person__user=request.user, status__gt=0, ), self.mc_pk == None, ]), ]) # Transitions @fsm_log_by @fsm_log_description @transition(field=status, source='*', target=STATUS.active) def activate(self, description=None, *args, **kwargs): """Activate the Member.""" return @fsm_log_by @fsm_log_description @transition(field=status, source='*', target=STATUS.inactive) def deactivate(self, description=None, *args, **kwargs): """Deactivate the Member.""" return
class Aggregate(ChangeLoggedModel, CustomFieldModel): """ An aggregate exists at the root level of the IP address space hierarchy in NetBox. Aggregates are used to organize the hierarchy and track the overall utilization of available address space. Each Aggregate is assigned to a RIR. """ family = models.PositiveSmallIntegerField( choices=IPAddressFamilyChoices ) prefix = IPNetworkField() rir = models.ForeignKey( to='ipam.RIR', on_delete=models.PROTECT, related_name='aggregates', verbose_name='RIR' ) date_added = models.DateField( blank=True, null=True ) description = models.CharField( max_length=100, blank=True ) custom_field_values = GenericRelation( to='extras.CustomFieldValue', content_type_field='obj_type', object_id_field='obj_id' ) tags = TaggableManager(through=TaggedItem) csv_headers = ['prefix', 'rir', 'date_added', 'description'] clone_fields = [ 'rir', 'date_added', 'description', ] class Meta: ordering = ('family', 'prefix', 'pk') # (family, prefix) may be non-unique def __str__(self): return str(self.prefix) def get_absolute_url(self): return reverse('ipam:aggregate', args=[self.pk]) def clean(self): if self.prefix: # Clear host bits from prefix self.prefix = self.prefix.cidr # /0 masks are not acceptable if self.prefix.prefixlen == 0: raise ValidationError({ 'prefix': "Cannot create aggregate with /0 mask." }) # Ensure that the aggregate being added is not covered by an existing aggregate covering_aggregates = Aggregate.objects.filter(prefix__net_contains_or_equals=str(self.prefix)) if self.pk: covering_aggregates = covering_aggregates.exclude(pk=self.pk) if covering_aggregates: raise ValidationError({ 'prefix': "Aggregates cannot overlap. {} is already covered by an existing aggregate ({}).".format( self.prefix, covering_aggregates[0] ) }) # Ensure that the aggregate being added does not cover an existing aggregate covered_aggregates = Aggregate.objects.filter(prefix__net_contained=str(self.prefix)) if self.pk: covered_aggregates = covered_aggregates.exclude(pk=self.pk) if covered_aggregates: raise ValidationError({ 'prefix': "Aggregates cannot overlap. {} covers an existing aggregate ({}).".format( self.prefix, covered_aggregates[0] ) }) def save(self, *args, **kwargs): if self.prefix: # Infer address family from IPNetwork object self.family = self.prefix.version super().save(*args, **kwargs) def to_csv(self): return ( self.prefix, self.rir.name, self.date_added, self.description, ) def get_utilization(self): """ Determine the prefix utilization of the aggregate and return it as a percentage. """ queryset = Prefix.objects.filter(prefix__net_contained_or_equal=str(self.prefix)) child_prefixes = netaddr.IPSet([p.prefix for p in queryset]) return int(float(child_prefixes.size) / self.prefix.size * 100)
class Issue(GcdData): class Meta: app_label = 'gcd' ordering = ['series', 'sort_code'] unique_together = ('series', 'sort_code') # Issue identification number = models.CharField(max_length=50, db_index=True) title = models.CharField(max_length=255, db_index=True) no_title = models.BooleanField(default=False, db_index=True) volume = models.CharField(max_length=50, db_index=True) no_volume = models.BooleanField(default=False, db_index=True) volume_not_printed = models.BooleanField(default=False) display_volume_with_number = models.BooleanField(default=False, db_index=True) isbn = models.CharField(max_length=32, db_index=True) no_isbn = models.BooleanField(default=False, db_index=True) valid_isbn = models.CharField(max_length=13, db_index=True) variant_of = models.ForeignKey('self', on_delete=models.CASCADE, null=True, related_name='variant_set') variant_name = models.CharField(max_length=255) barcode = models.CharField(max_length=38, db_index=True) no_barcode = models.BooleanField(default=False) rating = models.CharField(max_length=255, default='', db_index=True) no_rating = models.BooleanField(default=False, db_index=True) # Dates and sorting publication_date = models.CharField(max_length=255) key_date = models.CharField(max_length=10, db_index=True) on_sale_date = models.CharField(max_length=10, db_index=True) on_sale_date_uncertain = models.BooleanField(default=False) sort_code = models.IntegerField(db_index=True) indicia_frequency = models.CharField(max_length=255) no_indicia_frequency = models.BooleanField(default=False, db_index=True) # Price, page count and format fields price = models.CharField(max_length=255) page_count = models.DecimalField(max_digits=10, decimal_places=3, null=True) page_count_uncertain = models.BooleanField(default=False) editing = models.TextField() no_editing = models.BooleanField(default=False, db_index=True) notes = models.TextField() keywords = TaggableManager() # Series and publisher links series = models.ForeignKey('Series', on_delete=models.CASCADE) indicia_publisher = models.ForeignKey(IndiciaPublisher, on_delete=models.CASCADE, null=True) indicia_pub_not_printed = models.BooleanField(default=False) brand = models.ForeignKey(Brand, on_delete=models.CASCADE, null=True) no_brand = models.BooleanField(default=False, db_index=True) indicia_printer = models.ManyToManyField(IndiciaPrinter) no_indicia_printer = models.BooleanField(default=False) image_resources = GenericRelation(Image) awards = GenericRelation(ReceivedAward) # In production, this is a tinyint(1) because the set of numbers # is very small. But syncdb produces an int(11). is_indexed = models.IntegerField(default=0, db_index=True) @property def indicia_image(self): img = Image.objects.filter( object_id=self.id, deleted=False, content_type=ContentType.objects.get_for_model(self), type__id=1) if img: return img.get() else: return None @property def soo_image(self): img = Image.objects.filter( object_id=self.id, deleted=False, content_type=ContentType.objects.get_for_model(self), type__id=2) if img: return img.get() else: return None @property def active_credits(self): return self.credits.exclude(deleted=True) def active_stories(self): return self.story_set.exclude(deleted=True) def _active_variants(self): return self.variant_set.exclude(deleted=True) def active_variants(self): return self._active_variants() def active_awards(self): return self.awards.exclude(deleted=True) def active_printers(self): return self.indicia_printer.all() def active_code_numbers(self): return self.code_number.all() def shown_stories(self): """ returns cover sequence and story sequences """ if self.variant_of: stories_from = self.variant_of else: stories_from = self stories = list(stories_from.active_stories().order_by( 'sequence_number').select_related( 'type', 'migration_status').prefetch_related('feature_object')) if self.series.is_comics_publication: if (len(stories) > 0) and stories[0].type.id == 6: cover_story = stories.pop(0) if self.variant_of: # can have only one sequence, the variant cover if self.active_stories().count(): cover_story = self.active_stories()[0] elif self.variant_of and len(list(self.active_stories())): cover_story = self.active_stories()[0] else: cover_story = None else: cover_story = None return cover_story, stories def _active_covers(self): if self.can_have_cover(): return self.cover_set.exclude(deleted=True) else: return self.cover_set.none() def active_covers(self): return self._active_covers() def variant_covers(self): """ returns the images from the variant issues """ from .cover import Cover if self.variant_of: variant_issues = list(self.variant_of.active_variants().exclude( id=self.id).values_list('id', flat=True)) else: variant_issues = list(self.active_variants().values_list( 'id', flat=True)) variant_covers = Cover.objects.filter(issue__id__in=variant_issues)\ .exclude(deleted=True) if self.variant_of: variant_covers |= self.variant_of.active_covers() return variant_covers def shown_covers(self): return self.active_covers(), self.variant_covers() def show_printer(self): first = True printers = '' for printer in self.active_printers(): if first: first = False else: printers += '; ' printers += '<a href="%s">%s</a>' % (printer.get_absolute_url(), esc(printer.name)) return mark_safe(printers) def has_keywords(self): if self.series.is_singleton: return self.keywords.exists() or self.series.has_keywords() return self.keywords.exists() def has_content(self): """ Simplifies UI checks for conditionals. Content fields """ return self.notes or \ self.variant_of or \ self.other_variants() or \ self.has_keywords() or \ self.has_reprints() or \ self.active_awards().count() def has_covers(self): return self.can_have_cover() and self.active_covers().exists() def can_have_cover(self): if self.series.is_comics_publication: return True if self.is_indexed in [INDEXED['full'], INDEXED['ten_percent']]: return True else: return False def other_variants(self): if self.variant_of: variants = self.variant_of.active_variants().exclude(id=self.id) else: variants = self.active_variants() return list(variants) def _get_prev_next_issue(self): """ Find the issues immediately before and after the given issue. """ prev_issue = None next_issue = None earlier_issues = self.series.active_base_issues()\ .filter(sort_code__lt=self.sort_code) earlier_issues = earlier_issues.order_by('-sort_code') if earlier_issues: prev_issue = earlier_issues[0] later_issues = self.series.active_base_issues()\ .filter(sort_code__gt=self.sort_code) later_issues = later_issues.order_by('sort_code') if later_issues: next_issue = later_issues[0] return [prev_issue, next_issue] def get_prev_next_issue(self): return self._get_prev_next_issue() def has_reprints(self, ignore=STORY_TYPES['preview']): """Simplifies UI checks for conditionals, notes and reprint fields""" return self.from_reprints.count() or \ self.to_reprints.exclude(target__type__id=ignore).count() or \ self.from_issue_reprints.count() or \ self.to_issue_reprints.count() def has_variants(self): return self.active_variants().exists() def has_dependents(self): # what about award_revisions ? has_non_story_deps = ( self.has_variants() or self.has_reprints(ignore=None) or self.cover_revisions.active_set().exists() or self.variant_revisions.active_set().exists() or self.origin_reprint_revisions.active_set().exists() or self.target_reprint_revisions.active_set().exists()) if has_non_story_deps: return True for story in self.active_stories(): has_story_deps = ( story.has_reprints(notes=False) or story.origin_reprint_revisions.active_set().exists() or story.target_reprint_revisions.active_set().exists()) if has_story_deps: return True return False def can_upload_variants(self): if self.has_covers(): currently_deleting = self.revisions.active_set() \ .filter(deleted=True).exists() return not currently_deleting else: return False def set_indexed_status(self): """ Sets the index status and returns the resulting stat change value. The return value of this method is intended for use in adjusting the "issue indexes" stat count. GCD model modules cannot import CountStats and set them directly due to circular dependencies. """ was_indexed = self.is_indexed if not self.variant_of: is_indexed = INDEXED['skeleton'] if Decimal(self.page_count or 0) > 0: total_count = self.active_stories()\ .aggregate(Sum('page_count'))['page_count__sum'] if total_count is None: total_count = 0 if (total_count > 0 and total_count >= Decimal('0.4') * self.page_count): is_indexed = INDEXED['full'] elif (total_count > 0 and total_count >= Decimal('0.1') * self.page_count): is_indexed = INDEXED['ten_percent'] if (is_indexed not in [INDEXED['full'], INDEXED['ten_percent']] and self.active_stories().filter(type=StoryType.objects.get( name='comic story')).exists()): is_indexed = INDEXED['partial'] if is_indexed == INDEXED['full']: if self.page_count_uncertain or self.active_stories()\ .filter(page_count_uncertain=True).exists(): is_indexed = INDEXED['partial'] if self.is_indexed != is_indexed: self.is_indexed = is_indexed self.save() if self.active_variants(): for variant in self.active_variants(): variant.is_indexed = is_indexed variant.save() index_delta = 0 if self.series.is_comics_publication: if not was_indexed and self.is_indexed: index_delta = 1 elif was_indexed and not self.is_indexed: index_delta = -1 return index_delta _update_stats = True def stat_counts(self): """ Returns all count values relevant to this issue. Includes counts for the issue itself. Non-comics publications return statistics only for stories and covers, as non-comics issues do not count towards stats. Note that we have a special value "series issues", because non-variant issues are counted differently with respect to series than in general. A series always counts its own non-variant issues, even when the series is not a comics publication. """ if self.deleted: return {} counts = { 'stories': self.active_stories().count(), 'covers': self.active_covers().count(), } if not self.variant_of_id: counts['series issues'] = 1 if self.series.is_comics_publication: if self.variant_of_id: counts['variant issues'] = 1 else: counts['issues'] = 1 if self.is_indexed != INDEXED['skeleton']: counts['issue indexes'] = 1 return counts def get_absolute_url(self): return urlresolvers.reverse('show_issue', kwargs={'issue_id': self.id}) @property def full_descriptor(self): if self.variant_name: return "%s [%s]" % (self.issue_descriptor, self.variant_name) if self.active_code_numbers().filter(number_type__id=1): return "%s (%s)" % (self.issue_descriptor, self.active_code_numbers().get( number_type__id=1).number) return self.issue_descriptor @property def issue_descriptor(self): return issue_descriptor(self) @property def display_full_descriptor(self): number = self.full_descriptor if number: return '#' + number else: return '' @property def display_number(self): number = self.issue_descriptor if number: return '#' + number else: return '' def full_name(self, variant_name=True): if variant_name and self.variant_name: return '%s %s [%s]' % (self.series.full_name(), self.display_number, self.variant_name) if self.active_code_numbers().filter(number_type__id=1): return "%s %s (%s)" % ( self.series.full_name(), self.display_number, self.active_code_numbers().get(number_type__id=1).number) return '%s %s' % (self.series.full_name(), self.display_number) def full_name_with_link(self, publisher=False): name_link = self.series.full_name_with_link(publisher) return mark_safe( '%s <a href="%s">%s</a>' % (name_link, self.get_absolute_url(), esc(self.display_number))) def show_series_and_issue_link(self): if self.display_number: issue_number = '%s' % (esc(self.display_number)) else: issue_number = '' if self.variant_name: issue_number = '%s [%s]' % (issue_number, esc(self.variant_name)) if issue_number: issue_number = '<a href="%s">%s</a>' % (self.get_absolute_url(), issue_number) return mark_safe( '<a href="%s">%s</a> (%s series) %s' % (self.series.get_absolute_url(), esc( self.series.name), esc(self.series.year_began), issue_number)) def short_name(self): if self.variant_name: return '%s %s [%s]' % (self.series.name, self.display_number, self.variant_name) else: return '%s %s' % (self.series.name, self.display_number) def __str__(self): if self.variant_name: return '%s %s [%s]' % (self.series, self.display_number, self.variant_name) else: return '%s %s' % (self.series, self.display_number)
def setup_generic_relations(model_actor, relation_methods): """ 注册过的 actor_model_class 实例可以如此操作: actor.METHOD(target) # 建立 target 的特定 RELATION actor.dont_METHOD(target) # 或 no_METHOD 取消 target 的特定 RELATION # actor.actor_relations.filter(...) actor.METHOD_relations(...) # AS actor 的特定 RELATION filter actor.has_METHOD(target) # 与 target 是否存在特定 RELATION target_model_instance 可以有如下操作: target.target_relations.filter(...) target.METHOD_related(...) # AS target 的特定 RELATION filter target.was_METHOD(actor) # 与 actor 是否存在特定 RELATION owner_model_instance 可以有如下操作: owner.owner_relations.filter(...) owner.METHOD_related(...) # AS owner 的特定 RELATION filter """ from qrelation import models # rel = GenericRelation(models.Relation, content_type_field='actor_type', object_id_field='actor_id', related_query_name='relations_with_%s_as_%s' % (label(model_actor), 'actor')) rel.contribute_to_class(model_actor, 'actor_relations') # actor_type = get_contenttype(model_actor) for method, kwargs in relation_methods.items(): # 'follow': { # 'relation': models.REL_USER_FOLLOW, # 'target': 'quser.User', # } model_target = validate(kwargs.pop('target')) if not hasattr(model_target, 'target_relations'): rel = GenericRelation(models.Relation, content_type_field='target_type', object_id_field='target_id', related_query_name='relations_with_%s_as_%s' % (label(model_target), 'target')) rel.contribute_to_class(model_target, 'target_relations') model_owner = kwargs.pop('owner', None) if model_owner: model_owner = validate(model_owner) owner_type = get_contenttype(model_owner) if model_owner and not hasattr(model_owner, 'owner_relations'): rel = GenericRelation(models.Relation, content_type_field='owner_type', object_id_field='owner_id', related_query_name='relations_with_%s_as_%s' % (label(model_owner), 'owner')) rel.contribute_to_class(model_owner, 'owner_relations') # target_type = get_contenttype(model_target) relation, owner_field = kwargs.pop('relation'), kwargs.pop('owner_field', None) # 建立 relation setattr(model_actor, method, functools.partialmethod( relate, relation=relation, target_type=target_type, model_target=model_target, actor_type=actor_type, owner_field=owner_field)) cancel_method = functools.partialmethod( relate, relation=relation, target_type=target_type, model_target=model_target, actor_type=actor_type, deleted=True) # 取消 relation setattr(model_actor, 'dont_%s' % method, cancel_method) setattr(model_actor, 'no_%s' % method, cancel_method) # 给 actor_model 增加类方法 METHOD_relations setattr(model_actor, '%s_relations' % method, functools.partialmethod( filter_relations, relation=relation)) # 给 actor_model 增加类方法 has_METHOD setattr(model_actor, 'has_%s' % method, functools.partialmethod( check_relation, relation=relation, target_type=target_type, model_target=model_target)) # 给 target_model 增加类方法 METHOD_related setattr(model_target, '%s_related' % method, functools.partialmethod( filter_related, relation=relation)) # 给 target_model 增加类方法 was_METHOD setattr(model_target, 'was_%s' % method, functools.partialmethod( check_related, relation=relation, actor_type=actor_type, model_actor=model_actor)) if not model_owner: continue # 给 owner_model 增加类方法 setattr(model_owner, '%s_related' % method, functools.partialmethod( filter_owner, relation=relation))
class GenRelReference(models.Model): references = GenericRelation(ReferencedByGenRel)
class BasePage(TendenciBaseModel): guid = models.CharField(max_length=40) title = models.CharField(max_length=500, blank=True) slug = SlugField(_('URL Path')) header_image = models.ForeignKey('HeaderImage', null=True) content = tinymce_models.HTMLField() view_contact_form = models.BooleanField(default=False) design_notes = models.TextField(_('Design Notes'), blank=True) syndicate = models.BooleanField(_('Include in RSS feed'), default=False) template = models.CharField(_('Template'), max_length=50, blank=True) tags = TagField(blank=True) meta = models.OneToOneField(MetaTags, null=True) categories = GenericRelation(CategoryItem, object_id_field="object_id", content_type_field="content_type") class Meta: abstract = True app_label = 'pages' def save(self, *args, **kwargs): if not self.guid: self.guid = str(uuid.uuid1()) super(BasePage, self).save(*args, **kwargs) if self.header_image: if self.is_public(): set_s3_file_permission(self.header_image.file, public=True) else: set_s3_file_permission(self.header_image.file, public=False) def __unicode__(self): return self.title def get_header_image_url(self): if not self.header_image: return '' if self.is_public(): return self.header_image.file.url return reverse('page.header_image', args=[self.id]) def is_public(self): return all([ self.allow_anonymous_view, self.status, self.status_detail in ['active'] ]) @property def category_set(self): items = {} for cat in self.categories.select_related('category__name', 'parent__name'): if cat.category: items["category"] = cat.category elif cat.parent: items["sub_category"] = cat.parent return items @property def version(self): if self.status and self.status_detail: return self.status_detail + '-' + str(self.pk) + ' ' + str( self.create_dt) elif not self.status: return 'deleted-' + str(self.pk) + ' ' + str(self.create_dt) return ''
class Event(models.Model): ''' Container model for general metadata and associated ``Occurrence`` entries. ''' title = models.CharField(_('title'), max_length=32) location = models.ForeignKey(BookingLocation, on_delete=models.CASCADE, limit_choices_to={'active': True}) description = models.CharField(_('description'), max_length=100, blank=True) notes = GenericRelation(Note, verbose_name=_('notes')) #=========================================================================== class Meta: verbose_name = _('event') verbose_name_plural = _('events') ordering = ('title', ) #--------------------------------------------------------------------------- def __str__(self): return self.title #--------------------------------------------------------------------------- def get_absolute_url(self): return reverse('swingtime-event', args=[self.location.slug, str(self.id)]) #--------------------------------------------------------------------------- def add_occurrences(self, start_time, end_time, **rrule_params): ''' Add one or more occurences to the event using a comparable API to ``dateutil.rrule``. If ``rrule_params`` does not contain a ``freq``, one will be defaulted to ``rrule.DAILY``. Because ``rrule.rrule`` returns an iterator that can essentially be unbounded, we need to slightly alter the expected behavior here in order to enforce a finite number of occurrence creation. If both ``count`` and ``until`` entries are missing from ``rrule_params``, only a single ``Occurrence`` instance will be created using the exact ``start_time`` and ``end_time`` values. ''' rrule_params.setdefault('freq', rrule.DAILY) if 'count' not in rrule_params and 'until' not in rrule_params: self.occurrence_set.create(start_time=start_time, end_time=end_time) else: # weird things can happen with timezones here if we hit # a daylight savings time transition... # So make everything naive and then convert back to aware. start_time = force_naive(start_time) end_time = force_naive(end_time) if 'until' in rrule_params: rrule_params['until'] = force_naive(rrule_params['until']) delta = end_time - start_time for ev in rrule.rrule(dtstart=start_time, **rrule_params): ev_start = force_aware(ev) ev_end = force_aware(ev + delta) self.occurrence_set.create(start_time=ev_start, end_time=ev_end) #--------------------------------------------------------------------------- def upcoming_occurrences(self): ''' Return all occurrences that are set to start on or after the current time. ''' return self.occurrence_set.filter(start_time__gte=datetime_now()) #--------------------------------------------------------------------------- def next_occurrence(self): ''' Return the single occurrence set to start on or after the current time if available, otherwise ``None``. ''' upcoming = self.upcoming_occurrences() return upcoming and upcoming[0] or None #--------------------------------------------------------------------------- def daily_occurrences(self, dt=None): ''' Convenience method wrapping ``Occurrence.objects.daily_occurrences``. ''' return Occurrence.objects.daily_occurrences(dt=dt, event=self)
class EmbalagemProduto(Nomeavel, TemChaveExterna): item = GenericRelation('ItemCadastro', related_query_name='embalagem_produto') def __str__(self): return self.nome
class Fabricante(Nomeavel, TemChaveExterna): item = GenericRelation('ItemCadastro', related_query_name='fabricante') def __str__(self): return self.nome
class ScriptMetaData(CoreMetaData): scriptspecificmetadata = GenericRelation(ScriptSpecificMetadata) @property def resource(self): return ScriptResource.objects.filter(object_id=self.id).first() @property def program(self): return self.scriptspecificmetadata.all().first() @property def script_specific_metadata(self): return self.program @property def serializer(self): """Return an instance of rest_framework Serializer for self """ from serializers import ScriptMetaDataSerializer return ScriptMetaDataSerializer(self) @classmethod def parse_for_bulk_update(cls, metadata, parsed_metadata): """Overriding the base class method""" CoreMetaData.parse_for_bulk_update(metadata, parsed_metadata) keys_to_update = metadata.keys() if 'scriptspecificmetadata' in keys_to_update: parsed_metadata.append({ "scriptspecificmetadata": metadata.pop('scriptspecificmetadata') }) @classmethod def get_supported_element_names(cls): elements = super(ScriptMetaData, cls).get_supported_element_names() elements.append('ScriptSpecificMetadata') return elements def has_all_required_elements(self): if self.get_required_missing_elements(): return False return True def get_required_missing_elements(self): # show missing required meta missing_required_elements = super( ScriptMetaData, self).get_required_missing_elements() if not self.program: missing_required_elements.append('Script Language') missing_required_elements.append('Programming Language Version') else: if not self.program.scriptLanguage: missing_required_elements.append('Script Language') if not self.program.languageVersion: missing_required_elements.append( 'Programming Language Version') return missing_required_elements def update(self, metadata, user): # overriding the base class update method for bulk update of metadata from forms import ScriptFormValidation super(ScriptMetaData, self).update(metadata, user) attribute_mappings = {'scriptspecificmetadata': 'program'} with transaction.atomic(): # update/create non-repeatable element for element_name in attribute_mappings.keys(): for dict_item in metadata: if element_name in dict_item: validation_form = ScriptFormValidation( dict_item[element_name]) if not validation_form.is_valid(): err_string = self.get_form_errors_as_string( validation_form) raise ValidationError(err_string) element_property_name = attribute_mappings[ element_name] self.update_non_repeatable_element( element_name, metadata, element_property_name) break def get_xml(self, pretty_print=True, include_format_elements=True): # get the xml string for R Script xml_string = super(ScriptMetaData, self).get_xml(pretty_print=pretty_print) # create etree element RDF_ROOT = etree.fromstring(xml_string) # get the root 'Description' element, which contains all other elements container = RDF_ROOT.find('rdf:Description', namespaces=self.NAMESPACES) if self.program: if self.program.scriptReleaseDate: script_release_date = etree.SubElement( container, '{%s}scriptReleaseDate' % self.NAMESPACES['hsterms']) script_release_date.text = self.program.scriptReleaseDate.isoformat( ) script_language = etree.SubElement( container, '{%s}scriptLanguage' % self.NAMESPACES['hsterms']) script_language.text = self.program.scriptLanguage language_version = etree.SubElement( container, '{%s}languageVersion' % self.NAMESPACES['hsterms']) language_version.text = self.program.scriptVersion script_version = etree.SubElement( container, '{%s}scriptVersion' % self.NAMESPACES['hsterms']) script_version.text = self.program.scriptVersion script_dependencies = etree.SubElement( container, '{%s}scriptDependencies' % self.NAMESPACES['hsterms']) script_dependencies.text = self.program.scriptVersion script_code_repository = etree.SubElement( container, '{%s}scriptCodeRepository' % self.NAMESPACES['hsterms']) script_code_repository.text = self.program.scriptCodeRepository xml_string = etree.tostring(RDF_ROOT, pretty_print=pretty_print) return xml_string
class Place(dalmeUuid): std_name = models.CharField(max_length=255) type = models.IntegerField(db_index=True) attributes = GenericRelation('Attribute') instances = GenericRelation('Entity_phrase') tags = GenericRelation('Tag')
class Marca(Nomeavel, TemChaveExterna): item = GenericRelation('ItemCadastro', related_query_name='marca') def __str__(self): return self.nome
class VRF(ChangeLoggedModel, CustomFieldModel): """ A virtual routing and forwarding (VRF) table represents a discrete layer three forwarding domain (e.g. a routing table). Prefixes and IPAddresses can optionally be assigned to VRFs. (Prefixes and IPAddresses not assigned to a VRF are said to exist in the "global" table.) """ name = models.CharField( max_length=50 ) rd = models.CharField( max_length=VRF_RD_MAX_LENGTH, unique=True, blank=True, null=True, verbose_name='Route distinguisher' ) tenant = models.ForeignKey( to='tenancy.Tenant', on_delete=models.PROTECT, related_name='vrfs', blank=True, null=True ) enforce_unique = models.BooleanField( default=True, verbose_name='Enforce unique space', help_text='Prevent duplicate prefixes/IP addresses within this VRF' ) description = models.CharField( max_length=100, blank=True ) custom_field_values = GenericRelation( to='extras.CustomFieldValue', content_type_field='obj_type', object_id_field='obj_id' ) tags = TaggableManager(through=TaggedItem) csv_headers = ['name', 'rd', 'tenant', 'enforce_unique', 'description'] clone_fields = [ 'tenant', 'enforce_unique', 'description', ] class Meta: ordering = ('name', 'rd', 'pk') # (name, rd) may be non-unique verbose_name = 'VRF' verbose_name_plural = 'VRFs' def __str__(self): return self.display_name or super().__str__() def get_absolute_url(self): return reverse('ipam:vrf', args=[self.pk]) def to_csv(self): return ( self.name, self.rd, self.tenant.name if self.tenant else None, self.enforce_unique, self.description, ) @property def display_name(self): if self.rd: return "{} ({})".format(self.name, self.rd) return self.name
class IPAddress(ChangeLoggedModel, CustomFieldModel): """ An IPAddress represents an individual IPv4 or IPv6 address and its mask. The mask length should match what is configured in the real world. (Typically, only loopback interfaces are configured with /32 or /128 masks.) Like Prefixes, IPAddresses can optionally be assigned to a VRF. An IPAddress can optionally be assigned to an Interface. Interfaces can have zero or more IPAddresses assigned to them. An IPAddress can also optionally point to a NAT inside IP, designating itself as a NAT outside IP. This is useful, for example, when mapping public addresses to private addresses. When an Interface has been assigned an IPAddress which has a NAT outside IP, that Interface's Device can use either the inside or outside IP as its primary IP. """ family = models.PositiveSmallIntegerField( choices=IPAddressFamilyChoices, editable=False ) address = IPAddressField( help_text='IPv4 or IPv6 address (with mask)' ) vrf = models.ForeignKey( to='ipam.VRF', on_delete=models.PROTECT, related_name='ip_addresses', blank=True, null=True, verbose_name='VRF' ) tenant = models.ForeignKey( to='tenancy.Tenant', on_delete=models.PROTECT, related_name='ip_addresses', blank=True, null=True ) status = models.CharField( max_length=50, choices=IPAddressStatusChoices, default=IPAddressStatusChoices.STATUS_ACTIVE, help_text='The operational status of this IP' ) role = models.CharField( max_length=50, choices=IPAddressRoleChoices, blank=True, help_text='The functional role of this IP' ) interface = models.ForeignKey( to='dcim.Interface', on_delete=models.CASCADE, related_name='ip_addresses', blank=True, null=True ) nat_inside = models.OneToOneField( to='self', on_delete=models.SET_NULL, related_name='nat_outside', blank=True, null=True, verbose_name='NAT (Inside)', help_text='The IP for which this address is the "outside" IP' ) dns_name = models.CharField( max_length=255, blank=True, validators=[DNSValidator], verbose_name='DNS Name', help_text='Hostname or FQDN (not case-sensitive)' ) description = models.CharField( max_length=100, blank=True ) custom_field_values = GenericRelation( to='extras.CustomFieldValue', content_type_field='obj_type', object_id_field='obj_id' ) objects = IPAddressManager() tags = TaggableManager(through=TaggedItem) csv_headers = [ 'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface_name', 'is_primary', 'dns_name', 'description', ] clone_fields = [ 'vrf', 'tenant', 'status', 'role', 'description', ] STATUS_CLASS_MAP = { 'active': 'primary', 'reserved': 'info', 'deprecated': 'danger', 'dhcp': 'success', } ROLE_CLASS_MAP = { 'loopback': 'default', 'secondary': 'primary', 'anycast': 'warning', 'vip': 'success', 'vrrp': 'success', 'hsrp': 'success', 'glbp': 'success', 'carp': 'success', } class Meta: ordering = ('family', 'address', 'pk') # (family, address) may be non-unique verbose_name = 'IP address' verbose_name_plural = 'IP addresses' def __str__(self): return str(self.address) def get_absolute_url(self): return reverse('ipam:ipaddress', args=[self.pk]) def get_duplicates(self): return IPAddress.objects.filter(vrf=self.vrf, address__net_host=str(self.address.ip)).exclude(pk=self.pk) def clean(self): if self.address: # /0 masks are not acceptable if self.address.prefixlen == 0: raise ValidationError({ 'address': "Cannot create IP address with /0 mask." }) # Enforce unique IP space (if applicable) if self.role not in IPADDRESS_ROLES_NONUNIQUE and (( self.vrf is None and settings.ENFORCE_GLOBAL_UNIQUE ) or ( self.vrf and self.vrf.enforce_unique )): duplicate_ips = self.get_duplicates() if duplicate_ips: raise ValidationError({ 'address': "Duplicate IP address found in {}: {}".format( "VRF {}".format(self.vrf) if self.vrf else "global table", duplicate_ips.first(), ) }) if self.pk: # Check for primary IP assignment that doesn't match the assigned device/VM device = Device.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first() if device: if self.interface is None: raise ValidationError({ 'interface': "IP address is primary for device {} but not assigned".format(device) }) elif (device.primary_ip4 == self or device.primary_ip6 == self) and self.interface.device != device: raise ValidationError({ 'interface': "IP address is primary for device {} but assigned to {} ({})".format( device, self.interface.device, self.interface ) }) vm = VirtualMachine.objects.filter(Q(primary_ip4=self) | Q(primary_ip6=self)).first() if vm: if self.interface is None: raise ValidationError({ 'interface': "IP address is primary for virtual machine {} but not assigned".format(vm) }) elif (vm.primary_ip4 == self or vm.primary_ip6 == self) and self.interface.virtual_machine != vm: raise ValidationError({ 'interface': "IP address is primary for virtual machine {} but assigned to {} ({})".format( vm, self.interface.virtual_machine, self.interface ) }) def save(self, *args, **kwargs): # Record address family if isinstance(self.address, netaddr.IPNetwork): self.family = self.address.version # Force dns_name to lowercase self.dns_name = self.dns_name.lower() super().save(*args, **kwargs) def to_objectchange(self, action): # Annotate the assigned Interface (if any) try: parent_obj = self.interface except ObjectDoesNotExist: parent_obj = None return ObjectChange( changed_object=self, object_repr=str(self), action=action, related_object=parent_obj, object_data=serialize_object(self) ) def to_csv(self): # Determine if this IP is primary for a Device if self.family == 4 and getattr(self, 'primary_ip4_for', False): is_primary = True elif self.family == 6 and getattr(self, 'primary_ip6_for', False): is_primary = True else: is_primary = False return ( self.address, self.vrf.name if self.vrf else None, self.tenant.name if self.tenant else None, self.get_status_display(), self.get_role_display(), self.device.identifier if self.device else None, self.virtual_machine.name if self.virtual_machine else None, self.interface.name if self.interface else None, is_primary, self.dns_name, self.description, ) def _set_mask_length(self, value): """ Expose the IPNetwork object's prefixlen attribute on the parent model so that it can be manipulated directly, e.g. for bulk editing. """ if self.address is not None: self.address.prefixlen = value mask_length = property(fset=_set_mask_length) @property def device(self): if self.interface: return self.interface.device return None @property def virtual_machine(self): if self.interface: return self.interface.virtual_machine return None def get_status_class(self): return self.STATUS_CLASS_MAP.get(self.status) def get_role_class(self): return self.ROLE_CLASS_MAP[self.role]
class Prefix(ChangeLoggedModel, CustomFieldModel): """ A Prefix represents an IPv4 or IPv6 network, including mask length. Prefixes can optionally be assigned to Sites and VRFs. A Prefix must be assigned a status and may optionally be assigned a used-define Role. A Prefix can also be assigned to a VLAN where appropriate. """ family = models.PositiveSmallIntegerField( choices=IPAddressFamilyChoices, editable=False ) prefix = IPNetworkField( help_text='IPv4 or IPv6 network with mask' ) site = models.ForeignKey( to='dcim.Site', on_delete=models.PROTECT, related_name='prefixes', blank=True, null=True ) vrf = models.ForeignKey( to='ipam.VRF', on_delete=models.PROTECT, related_name='prefixes', blank=True, null=True, verbose_name='VRF' ) tenant = models.ForeignKey( to='tenancy.Tenant', on_delete=models.PROTECT, related_name='prefixes', blank=True, null=True ) vlan = models.ForeignKey( to='ipam.VLAN', on_delete=models.PROTECT, related_name='prefixes', blank=True, null=True, verbose_name='VLAN' ) status = models.CharField( max_length=50, choices=PrefixStatusChoices, default=PrefixStatusChoices.STATUS_ACTIVE, verbose_name='Status', help_text='Operational status of this prefix' ) role = models.ForeignKey( to='ipam.Role', on_delete=models.SET_NULL, related_name='prefixes', blank=True, null=True, help_text='The primary function of this prefix' ) is_pool = models.BooleanField( verbose_name='Is a pool', default=False, help_text='All IP addresses within this prefix are considered usable' ) description = models.CharField( max_length=100, blank=True ) custom_field_values = GenericRelation( to='extras.CustomFieldValue', content_type_field='obj_type', object_id_field='obj_id' ) objects = PrefixQuerySet.as_manager() tags = TaggableManager(through=TaggedItem) csv_headers = [ 'prefix', 'vrf', 'tenant', 'site', 'vlan_group', 'vlan_vid', 'status', 'role', 'is_pool', 'description', ] clone_fields = [ 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'description', ] STATUS_CLASS_MAP = { 'container': 'default', 'active': 'primary', 'reserved': 'info', 'deprecated': 'danger', } class Meta: ordering = (F('vrf').asc(nulls_first=True), 'family', 'prefix', 'pk') # (vrf, family, prefix) may be non-unique verbose_name_plural = 'prefixes' def __str__(self): return str(self.prefix) def get_absolute_url(self): return reverse('ipam:prefix', args=[self.pk]) def clean(self): if self.prefix: # /0 masks are not acceptable if self.prefix.prefixlen == 0: raise ValidationError({ 'prefix': "Cannot create prefix with /0 mask." }) # Disallow host masks if self.prefix.version == 4 and self.prefix.prefixlen == 32: raise ValidationError({ 'prefix': "Cannot create host addresses (/32) as prefixes. Create an IPv4 address instead." }) elif self.prefix.version == 6 and self.prefix.prefixlen == 128: raise ValidationError({ 'prefix': "Cannot create host addresses (/128) as prefixes. Create an IPv6 address instead." }) # Enforce unique IP space (if applicable) if (self.vrf is None and settings.ENFORCE_GLOBAL_UNIQUE) or (self.vrf and self.vrf.enforce_unique): duplicate_prefixes = self.get_duplicates() if duplicate_prefixes: raise ValidationError({ 'prefix': "Duplicate prefix found in {}: {}".format( "VRF {}".format(self.vrf) if self.vrf else "global table", duplicate_prefixes.first(), ) }) def save(self, *args, **kwargs): if isinstance(self.prefix, netaddr.IPNetwork): # Clear host bits from prefix self.prefix = self.prefix.cidr # Record address family self.family = self.prefix.version super().save(*args, **kwargs) def to_csv(self): return ( self.prefix, self.vrf.name if self.vrf else None, self.tenant.name if self.tenant else None, self.site.name if self.site else None, self.vlan.group.name if self.vlan and self.vlan.group else None, self.vlan.vid if self.vlan else None, self.get_status_display(), self.role.name if self.role else None, self.is_pool, self.description, ) def _set_prefix_length(self, value): """ Expose the IPNetwork object's prefixlen attribute on the parent model so that it can be manipulated directly, e.g. for bulk editing. """ if self.prefix is not None: self.prefix.prefixlen = value prefix_length = property(fset=_set_prefix_length) def get_status_class(self): return self.STATUS_CLASS_MAP.get(self.status) def get_duplicates(self): return Prefix.objects.filter(vrf=self.vrf, prefix=str(self.prefix)).exclude(pk=self.pk) def get_child_prefixes(self): """ Return all Prefixes within this Prefix and VRF. If this Prefix is a container in the global table, return child Prefixes belonging to any VRF. """ if self.vrf is None and self.status == PrefixStatusChoices.STATUS_CONTAINER: return Prefix.objects.filter(prefix__net_contained=str(self.prefix)) else: return Prefix.objects.filter(prefix__net_contained=str(self.prefix), vrf=self.vrf) def get_child_ips(self): """ Return all IPAddresses within this Prefix and VRF. If this Prefix is a container in the global table, return child IPAddresses belonging to any VRF. """ if self.vrf is None and self.status == PrefixStatusChoices.STATUS_CONTAINER: return IPAddress.objects.filter(address__net_host_contained=str(self.prefix)) else: return IPAddress.objects.filter(address__net_host_contained=str(self.prefix), vrf=self.vrf) def get_available_prefixes(self): """ Return all available Prefixes within this prefix as an IPSet. """ prefix = netaddr.IPSet(self.prefix) child_prefixes = netaddr.IPSet([child.prefix for child in self.get_child_prefixes()]) available_prefixes = prefix - child_prefixes return available_prefixes def get_available_ips(self): """ Return all available IPs within this prefix as an IPSet. """ prefix = netaddr.IPSet(self.prefix) child_ips = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()]) available_ips = prefix - child_ips # All IP addresses within a pool are considered usable if self.is_pool: return available_ips # All IP addresses within a point-to-point prefix (IPv4 /31 or IPv6 /127) are considered usable if ( self.family == 4 and self.prefix.prefixlen == 31 # RFC 3021 ) or ( self.family == 6 and self.prefix.prefixlen == 127 # RFC 6164 ): return available_ips # Omit first and last IP address from the available set available_ips -= netaddr.IPSet([ netaddr.IPAddress(self.prefix.first), netaddr.IPAddress(self.prefix.last), ]) return available_ips def get_first_available_prefix(self): """ Return the first available child prefix within the prefix (or None). """ available_prefixes = self.get_available_prefixes() if not available_prefixes: return None return available_prefixes.iter_cidrs()[0] def get_first_available_ip(self): """ Return the first available IP within the prefix (or None). """ available_ips = self.get_available_ips() if not available_ips: return None return '{}/{}'.format(next(available_ips.__iter__()), self.prefix.prefixlen) def get_utilization(self): """ Determine the utilization of the prefix and return it as a percentage. For Prefixes with a status of "container", calculate utilization based on child prefixes. For all others, count child IP addresses. """ if self.status == PrefixStatusChoices.STATUS_CONTAINER: queryset = Prefix.objects.filter(prefix__net_contained=str(self.prefix), vrf=self.vrf) child_prefixes = netaddr.IPSet([p.prefix for p in queryset]) return int(float(child_prefixes.size) / self.prefix.size * 100) else: # Compile an IPSet to avoid counting duplicate IPs child_count = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()]).size prefix_size = self.prefix.size if self.family == 4 and self.prefix.prefixlen < 31 and not self.is_pool: prefix_size -= 2 return int(float(child_count) / prefix_size * 100)
class FriendList(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="user") friends = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name="friends") notifications = GenericRelation(Notification) def __str__(self): return self.user.username def add_friend(self, account): # Adding a friend into friend list if not account in self.friends.all(): self.friends.add(account) self.save() content_type = ContentType.objects.get_for_model(self) self.notifications.create( target=self.user, from_user=account, redirect_url=f"{settings.BASE_URL}/account/{account.pk}/", statement=f"You are now friend with {account.user}", content_type=content_type) self.save() # Creating a private chat room when two users become friends chat = find_or_create_private_chat(self.user, account) if not chat.is_active: chat.is_active = True chat.save() def remove_friend(self, account): # Removing a friend from the friend list if account in self.friends.all(): self.friends.remove(account) # Deactivating the private chat room when two users are no longer friends chat = find_or_create_private_chat(self.user, account) if chat.is_active: chat.is_active = False chat.save() def unfriend(self, removee): # Removing a friend remover_friends_list = self remover_friends_list.remove_friend(removee) friends_list = FriendList.objects.get(user=removee) friends_list.remove_friend(self.user) content_type = ContentType.objects.get_for_model(self) # Notification for removee friends_list.notifications.create( target=removee, from_user=self.user, redirect_url=f"{settings.BASE_URL}/account/{self.user.pk}/", statement=f"You are no longer friends with {self.user.username}.", content_type=content_type, ) # Notification for remover self.notifications.create( target=self.user, from_user=removee, redirect_url=f"{settings.BASE_URL}/account/{removee.pk}/", statement=f"You are no longer friends with {removee.username}.", content_type=content_type, ) @property def get_cname(self): """ For determining what kind of object is associated with a Notification """ return "FriendList" def is_mutual_friends(self, friend): # Getting the result if the friends are mutual if friend in self.friends.all(): return True return False