Ejemplo n.º 1
0
class PreserveModelMixin(ModifiedModelMixin):
    """Base model to handle core objects.

    Defines created, modified, and deleted fields.
    Prevents deletion of this model and flags for exclusion from results.
    """

    deleted_at = models.DateTimeField(
        auto_now=False,
        auto_now_add=False,
        blank=True,
        null=True,
        default=None,
    )

    objects = QueryManager(deleted_at__isnull=True)
    all_objects = models.Manager()
    deleted_objects = QueryManager(deleted_at__isnull=False)

    class Meta:
        abstract = True

    @property
    def is_deleted(self):
        return bool(self.deleted_at)

    def delete(self, *args, **kwargs):
        pre_delete.send(sender=self.__class__, instance=self)
        self.deleted_at = now()
        self.__class__.objects.filter(id=self.id).update(
            deleted_at=self.deleted_at, )
        post_delete.send(sender=self.__class__, instance=self)
Ejemplo n.º 2
0
class Branch(Publishable, Timestampable, models.Model):
    name = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200)
    address_1 = models.CharField(max_length=100)
    address_2 = models.CharField(max_length=100, blank=True, null=True)
    address_3 = models.CharField(max_length=100, blank=True, null=True)
    town_city = models.CharField(max_length=50)
    county = models.CharField(max_length=50)
    postcode = models.CharField(max_length=10)
    location = models.PointField()
    telephone = models.CharField(max_length=20)
    email = models.EmailField()
    details = models.TextField()
    opening_hours = models.TextField()
    objects = models.Manager()
    active = QueryManager(status=Publishable.STATUS_CHOICE_ACTIVE)
    inactive = QueryManager(status=Publishable.STATUS_CHOICE_INACTIVE)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name']
        verbose_name = _('branch')
        verbose_name_plural = _('branches')
Ejemplo n.º 3
0
class PropertyType(Publishable, models.Model):
    name = models.CharField(max_length=50)
    slug = models.SlugField(max_length=50)
    objects = models.Manager()
    active = QueryManager(status=Publishable.STATUS_CHOICE_ACTIVE)
    inactive = QueryManager(status=Publishable.STATUS_CHOICE_INACTIVE)

    def __str__(self):
        return self.name

    class Meta:
        pass
Ejemplo n.º 4
0
class Picture(Publishable, Timestampable, Orderable, models.Model):
    caption = models.CharField(max_length=200)
    attachment = models.ImageField(upload_to='pictures/')
    objects = models.Manager()
    active = QueryManager(status=Publishable.STATUS_CHOICE_ACTIVE)
    inactive = QueryManager(status=Publishable.STATUS_CHOICE_INACTIVE)

    def __str__(self):
        return self.caption

    class Meta:
        abstract = True
Ejemplo n.º 5
0
class Media(Publishable, models.Model):
    media_type = models.ForeignKey(MediaType)
    description = models.CharField(max_length=200)
    attachment = models.FileField(upload_to='media/')
    objects = models.Manager()
    active = QueryManager(status=Publishable.STATUS_CHOICE_ACTIVE)
    inactive = QueryManager(status=Publishable.STATUS_CHOICE_INACTIVE)

    def __str__(self):
        return self.description

    class Meta:
        abstract = True
Ejemplo n.º 6
0
class Post(models.Model):
    published = models.BooleanField(default=False)
    confirmed = models.BooleanField(default=False)
    order = models.IntegerField()

    objects = models.Manager()
    public = QueryManager(published=True)
    public_confirmed = QueryManager(
        models.Q(published=True) & models.Q(confirmed=True))
    public_reversed = QueryManager(published=True).order_by("-order")

    class Meta:
        ordering = ("order", )
Ejemplo n.º 7
0
class Post(models.Model):
    published = models.BooleanField()
    confirmed = models.BooleanField()
    order = models.IntegerField()

    objects = models.Manager()
    public = QueryManager(published=True)
    public_confirmed = QueryManager(
        models.Q(published=True) & models.Q(confirmed=True))
    public_reversed = QueryManager(published=True).order_by('-order')

    class Meta:
        ordering = ('order', )
Ejemplo n.º 8
0
class Excerpt(Authorable, TimeStampedModel, StatusModel):

    STATUS = Choices("approved", "flagged", "rejected")

    content = models.TextField(
        max_length=1200, help_text="Brief excerpt describing this protein")
    proteins = models.ManyToManyField("Protein",
                                      blank=True,
                                      related_name="excerpts")
    reference = models.ForeignKey(
        Reference,
        related_name="excerpts",
        null=True,
        on_delete=models.SET_NULL,
        help_text="Source of this excerpt",
    )

    objects = models.Manager()
    visible = QueryManager(~Q(status="rejected"))

    class Meta:
        ordering = ["reference__year", "created"]

    def __str__(self):
        ref = self.reference.citation if self.reference else ""
        return "{}: {}...".format(ref, self.content[:30])

    def get_absolute_url(self):
        return self.reference.get_absolute_url()
Ejemplo n.º 9
0
class ApiKey(TimeStampedModel, SoftDeletableModel):
    api_key = models.CharField(max_length=50,
                               null=False,
                               blank=True,
                               unique=True)

    live = ApiKeyManager()

    objects = SoftDeletableManager()
    removed = QueryManager(is_removed=True)

    @ecached_property('is_expire:{self.id}', 60)
    def is_expire(self):
        try:
            info = blockcypher.get_token_info(self.api_key)
            limits = info.get('limits', None)
            hits_history = info.get('hits_history', None)
            if not limits:
                return True
            if not hits_history:
                return False
            else:
                current_api_hour = sum(
                    [i['api/hour'] for i in hits_history if 'api/hour' in i])
                current_hooks_hour = sum([
                    i['hooks/hour'] for i in hits_history if 'hooks/hour' in i
                ])
                if current_api_hour < limits['api/hour'] and \
                   current_hooks_hour < limits['hooks/hour']:
                    return False
        except:
            return True

    def __str__(self):
        return self.api_key
Ejemplo n.º 10
0
class Transaction(TimeStampedModel, SoftDeletableModel):
    reference = models.CharField(max_length=255)

    wallet = models.ForeignKey('wallet.Wallet',
                               related_name='transactions',
                               on_delete=models.PROTECT)

    currency = models.CharField(
        max_length=settings.DEFAULT_CURRENCY_CODE_LENGTH,
        default=settings.DEFAULT_CURRENCY,
    )

    transaction_amount = models.DecimalField(
        max_digits=settings.DEFAULT_MAX_DIGITS,
        decimal_places=settings.DEFAULT_DECIMAL_PLACES,
    )
    amount = MoneyField(amount_field="transaction_amount",
                        currency_field="currency")

    is_pending = models.BooleanField(default=False)

    confirmed_at = models.DateTimeField(null=True, blank=True)

    objects = SoftDeletableManager()
    removed = QueryManager(is_removed=True)

    class Meta:
        ordering = ['wallet']

    def __str__(self):
        return '({})'.format(self.wallet)
Ejemplo n.º 11
0
class WorkflowModel(models.Model):
    """Mixin for models that have a workflow state."""

    _workflow_required = {}

    class WorkflowStates(models.TextChoices):
        PRIVATE = 'private', 'Private'
        PUBLISHED = 'published', 'Published'
        RETIRED = 'retired', 'Retired'

    STATUS = WorkflowStates.choices

    status = StatusField(
        db_index=True,
        help_text=
        'Hidden from students when private (visible, but not listed, when retired).',
    )

    status_changed = MonitorField(
        monitor='status',
        editable=False,
    )

    class Meta:
        abstract = True

    objects = QueryManager()
    private = QueryManager(status='private')
    published = QueryManager(status='published')
    retired = QueryManager(status='retired')
    public = QueryManager(status__in=['published', 'retired'])

    def clean(self):
        """Require that all state requirements are met."""

        super().clean()
        # noinspection PyUnresolvedReferences
        required = self._workflow_required.get(self.status)
        if not required:
            return True

        missing = {
            name: "Required to publish."
            for name in required if not getattr(self, name)
        }
        if missing:
            raise ValidationError(missing)
Ejemplo n.º 12
0
class Incident(StatusModel, TimeFramedModel, TimeStampedModel):
    title = models.CharField(max_length=200)
    description = models.CharField(max_length=500)
    STATUS = Choices('maintenance', 'partial_outage', 'major_outage')
    opened_by = models.ForeignKey(User,
                                  null=True,
                                  blank=True,
                                  on_delete=models.SET_NULL)
    system = models.ForeignKey(System, on_delete=models.CASCADE)

    objects = models.Manager()

    active = QueryManager(
        (models.Q(start__lte=now) | models.Q(start__isnull=True))
        & (models.Q(end__gte=now) | models.Q(end__isnull=True)))

    closed = QueryManager(models.Q(end__lte=now))

    def __str__(self):
        return f"{self.system} - {self.status} [{self.start}:{self.end}]"
Ejemplo n.º 13
0
class Item(TimeStampedModel):
    STATUS = Choices(u'yes', u'no', u'friends_only')
    name = models.CharField(max_length=255)
    slug = models.SlugField(max_length=255)
    description = models.TextField()
    status = StatusField()
    owner = models.ForeignKey(User, related_name=u'items')

    objects = models.Manager()
    public = QueryManager(status=u'yes').order_by(u'-modified')
    private = QueryManager(status=u'no').order_by(u'-modified')
    friends = QueryManager(status=u'friends_only').order_by(u'-modified')

    def __unicode__(self):
        return u'{0}'.format(self.name)

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)
        super(Item, self).save(*args, **kwargs)
Ejemplo n.º 14
0
class Membership(TimeStampedModel, StatusModel):
    STATUS = Choices(
        ('active', 'Active'),
        ('suspended', 'Suspended')
    )
    TITLES = Choices(
        ('storyteller', 'Storyteller'),
        ('coordinator', 'Coordinator')
    )

    user = ForeignKey(settings.AUTH_USER_MODEL, CASCADE, related_name="memberships")
    organization = ForeignKey(Organization, CASCADE, related_name="memberships")
    title = CharField(max_length=20, choices=TITLES, blank=True)
    external_id = CharField(max_length=20, blank=True, verbose_name='External ID')

    prestige_total = PositiveIntegerField(default=0, editable=False)
    prestige_level = ForeignKey('PrestigeLevel', SET_NULL, null=True, editable=False, related_name='memberships')

    objects = Manager()
    storytellers = QueryManager(title='storyteller')
    coordinators = QueryManager(title='coordinator')

    class Meta:
        unique_together = ('user', 'organization',)
        ordering = ('organization__name', 'user__username')

    def __str__(self):
        return f'#{self.external_id or self.pk} {self.user}'

    def recalculate_prestige(self):
        total = self.prestige.filter(amount__gt=0).all().aggregate(Sum('amount'))['amount__sum']
        self.prestige_total = total
        try:
            self.prestige_level = PrestigeLevel.objects.filter(
                organization=self.organization, prestige_required__lte=total).order_by('-prestige_required').first()
        except PrestigeLevel.DoesNotExist:
            logger.error(f'Missing prestige level for total >= {total}')
        self.save()
Ejemplo n.º 15
0
def add_status_query_managers(sender, **kwargs):
    """
    Add a Querymanager for each status item dynamically.

    """
    if not issubclass(sender, StatusModel):
        return
    for value, display in getattr(sender, 'STATUS', ()):
        if _field_exists(sender, value):
            raise ImproperlyConfigured(
                "StatusModel: Model '%s' has a field named '%s' which "
                "conflicts with a status of the same name." %
                (sender.__name__, value))
        sender.add_to_class(value, QueryManager(status=value))
Ejemplo n.º 16
0
class AgendaHora(models.Model):
    agenda = models.ForeignKey(Agenda, related_name='horarios', on_delete=models.PROTECT)
    hora = models.TimeField()
    disponivel = models.BooleanField('disponível', default=True, editable=False)

    objects = models.Manager()
    disponiveis = QueryManager(disponivel=True)

    class Meta:
        verbose_name = 'Horário'
        ordering = ['hora']
        unique_together = ['agenda', 'hora']

    def __str__(self):
        return self.hora.strftime('%H:%M')
Ejemplo n.º 17
0
def add_timeframed_query_manager(sender, **kwargs):
    """
    Add a QueryManager for a specific timeframe.

    """
    if not issubclass(sender, TimeFramedModel):
        return
    if _field_exists(sender, 'timeframed'):
        raise ImproperlyConfigured(
            "Model '%s' has a field named 'timeframed' "
            "which conflicts with the TimeFramedModel manager." %
            sender.__name__)
    sender.add_to_class(
        'timeframed',
        QueryManager((models.Q(start__lte=now) | models.Q(start__isnull=True))
                     & (models.Q(end__gte=now) | models.Q(end__isnull=True))))
Ejemplo n.º 18
0
def add_status_query_managers(sender, **kwargs):
    """
    Add a Querymanager for each status item dynamically.

    """
    if not issubclass(sender, StatusModel):
        return
    for value, display in getattr(sender, 'STATUS', ()):
        try:
            sender._meta.get_field(value)
            raise ImproperlyConfigured("StatusModel: Model '%s' has a field "
                                       "named '%s' which conflicts with a "
                                       "status of the same name."
                                       % (sender.__name__, value))
        except FieldDoesNotExist:
            pass
        sender.add_to_class(value, QueryManager(status=value))
Ejemplo n.º 19
0
def add_timeframed_query_manager(sender, **kwargs):
    """
    Add a QueryManager for a specific timeframe.

    """
    if not issubclass(sender, TimeFramedModel):
        return
    try:
        sender._meta.get_field('timeframed')
        raise ImproperlyConfigured("Model '%s' has a field named "
                                   "'timeframed' which conflicts with "
                                   "the TimeFramedModel manager."
                                   % sender.__name__)
    except FieldDoesNotExist:
        pass
    sender.add_to_class('timeframed', QueryManager(
        (models.Q(start__lte=now) | models.Q(start__isnull=True)) &
        (models.Q(end__gte=now) | models.Q(end__isnull=True))
    ))
Ejemplo n.º 20
0
def add_status_query_managers(sender, **kwargs):
    """
    Add a QueryManager for each status item dynamically.
    """
    if not issubclass(sender, StatusModel):
        return

    # First, get current manager name...
    default_manager = sender._meta.default_manager

    for value, display in getattr(sender, 'STATUSES', ()):
        if _field_exists(sender, value):
            raise ImproperlyConfigured(
                "StatusModel: Model '%s' has a field named '%s' which "
                "conflicts with a status of the same name." %
                (sender.__name__, value))
        sender.add_to_class(value, QueryManager(status=value))

    # ...then, put it back, as add_to_class is modifying the default manager!
    sender._meta.default_manager_name = default_manager.name
Ejemplo n.º 21
0
class Bank(TimeStampedModel, SoftDeletableModel):
    ACCOUNT_TYPE = Choices(('Savings', _('Savings')), ('Check', _('Check')))

    uuid = ShortUUIDField(max_length=8,
                          unique=True,
                          editable=False,
                          verbose_name='Public identifier')
    account_name = models.CharField(max_length=256, blank=True)
    account_holder_name = models.CharField(max_length=256, blank=True)
    bank_name = models.CharField(max_length=256, blank=True)
    account_number = models.PositiveIntegerField(
        unique=True,
        validators=[MinValueValidator(10000),
                    MaxValueValidator(999999999999)])
    account_type = models.CharField(choices=ACCOUNT_TYPE,
                                    default=ACCOUNT_TYPE.Savings,
                                    max_length=20)
    bank_branch_code = models.PositiveIntegerField(
        unique=False,
        validators=[MinValueValidator(1000),
                    MaxValueValidator(99999999)])
    user = models.OneToOneField(settings.AUTH_USER_MODEL,
                                related_name='bank',
                                unique=True,
                                on_delete=models.PROTECT)

    objects = SoftDeletableManager()
    removed = QueryManager(is_removed=True)

    class Meta:
        ordering = ['account_number']

    @property
    def account_reference_id(self):
        start_index = int(str(self.account_number)[4])
        account_reference = "ACC-" + str(
            self.account_number) + "-" + self.uuid[start_index:start_index + 4]
        return account_reference.upper()

    def __str__(self):
        return '({}) {}'.format(self.account_name.upper(), self.account_number)
Ejemplo n.º 22
0
class Advertisement(BaseModel):
    """The core advertisement an user creates"""
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    contact_email = models.EmailField(null=False)
    contact_phone = PhoneNumberField(blank=True)
    category = models.ForeignKey(to=Category, related_name='subcategory', on_delete=models.CASCADE)
    content = models.TextField(null=False)
    views = models.IntegerField(auto_created=True, default=0, null=False, editable=False)
    importance = models.IntegerField(auto_created=True, default=0, null=False)
    expires_date = models.DateTimeField()
    price = models.DecimalField(decimal_places=PRICE_MAX_DECIMALS, max_digits=PRICE_MAX_DIGITS)
    location_city = models.CharField(max_length=CITY_MAX_LENGTH)
    users_interested = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='users_interested', blank=True)
    published_date = models.DateTimeField(blank=True, null=True)

    tags = TaggableManager()
    objects = models.Manager()
    public = QueryManager(status='published').order_by('-published_date')

    class Meta:
        ordering = ['-created']
Ejemplo n.º 23
0
class Job(TimeStampedModel):
    """
    A job opening.

    """
    FULLTIME = 'ft'
    PARTTIME = 'pt'
    CONTRACT = 'co'
    ENGAGEMENT_TYPE_CHOICES = ((FULLTIME, 'full time'),
                               (PARTTIME, 'part time'), (CONTRACT, 'contract'))
    title = models.CharField(max_length=50)
    slug = models.SlugField(
        help_text='This field is populated from the title field.')
    description = models.TextField(help_text='Please use Markdown format.')
    requirements = models.TextField(help_text='Please use Markdown format.')
    posted_date = models.DateField()
    is_expired = models.BooleanField(
        default=False,
        help_text='Check if the job has been filled. '
        'Expired jobs will not be displayed to the public.')
    portfolio_required = models.BooleanField(default=True)
    engagement_type = models.CharField(choices=ENGAGEMENT_TYPE_CHOICES,
                                       default=FULLTIME,
                                       max_length=2)

    objects = models.Manager()
    active = QueryManager(is_expired=False)

    class Meta:
        get_latest_by = 'created'
        ordering = ('-created', )
        verbose_name_plural = 'Jobs'

    def __unicode__(self):
        return self.title

    @models.permalink
    def get_absolute_url(self):
        return ('jobs:detail', (), {'slug': self.slug})
Ejemplo n.º 24
0
class Common(models.Model):
    """
    Абстрактный класс. Содержит `статус` и `время создания / модификации` объекта.
    """

    DRAFT = 'draft'
    PUBLISHED = 'published'

    CHOICES_STATUS = (
        (DRAFT, 'Черновик'),
        (PUBLISHED, 'Опубликовано'),
    )

    status = models.CharField(
        'Статус',
        choices=CHOICES_STATUS,
        default=PUBLISHED,
        max_length=50,
    )

    created = models.DateTimeField(
        'Дата создания',
        auto_now_add=True,
    )

    modified = models.DateTimeField(
        'Дата изменения',
        auto_now=True,
    )

    objects = models.Manager()
    published = QueryManager(status=PUBLISHED)

    class Meta:
        abstract = True
        ordering = ('-created', )
Ejemplo n.º 25
0
class Act(TimeStampedModel, MonitorizedItem):
    """
    This is the base class for all the different act types: it contains the common fields for
    deliberations, interrogations, interpellations, motions, agendas and emendations.
  
    It is a ``TimeStampedModel``, so it tracks creation and modification timestamps for each record.

    The ``related_news`` attribute can be used  to fetch news related to it (or its subclasses) 
    from ``newscache.News``.

    Inheritance is done through multi-table inheritance, since browsing the whole set of acts may be useful.
    The default manager is the ``InheritanceManager`` (from package ``django-model-utils``_),
    that enables the ``select_subclasses()`` method, allowing the retrieval of subclasses, when needed.


    .. _django-model-utils: https://bitbucket.org/carljm/django-model-utils/src

    """

    idnum = models.CharField(
        max_length=64,
        blank=True,
        help_text=
        _("A string representing the identification number or sequence, used internally by the administration."
          ))
    title = models.CharField(_('title'), max_length=255, blank=True)
    adj_title = models.CharField(
        _('adjoint title'),
        max_length=255,
        blank=True,
        help_text=
        _("An adjoint title, added to further explain an otherwise cryptic title"
          ))
    presentation_date = models.DateField(
        _('presentation date'),
        null=True,
        help_text=_("Date of presentation, as stated in the act"))
    description = models.TextField(_('description'), blank=True)
    text = models.TextField(_('text'), blank=True)
    presenter_set = models.ManyToManyField(InstitutionCharge,
                                           blank=True,
                                           null=True,
                                           through='ActSupport',
                                           related_name='presented_act_set',
                                           verbose_name=_('presenters'))
    recipient_set = models.ManyToManyField(InstitutionCharge,
                                           blank=True,
                                           null=True,
                                           related_name='received_act_set',
                                           verbose_name=_('recipients'))
    emitting_institution = models.ForeignKey(
        Institution,
        related_name='emitted_act_set',
        verbose_name=_('emitting institution'))
    category_set = models.ManyToManyField(Category,
                                          verbose_name=_('categories'),
                                          blank=True,
                                          null=True)
    location_set = models.ManyToManyField(Location,
                                          through=TaggedActByLocation,
                                          verbose_name=_('locations'),
                                          blank=True,
                                          null=True)
    status_is_final = models.BooleanField(default=False)
    is_key = models.BooleanField(
        default=False,
        help_text=_("Specify whether this act should be featured"))

    objects = InheritanceManager()
    # use this manager to retrieve only key acts
    featured = QueryManager(is_key=True).order_by('-presentation_date')

    tag_set = TaggableManager(through=TaggedAct, blank=True)

    # manager to handle the list of news that have the act as related object
    related_news_set = generic.GenericRelation(
        News,
        content_type_field='related_content_type',
        object_id_field='related_object_pk')

    # manager to handle the list of monitoring having as content_object this instance
    monitoring_set = generic.GenericRelation(Monitoring,
                                             object_id_field='object_pk')

    def __unicode__(self):
        uc = u'%s' % (self.title, )
        if self.idnum:
            uc = u'%s - %s' % (self.idnum, uc)
        if self.adj_title:
            uc = u'%s (%s)' % (uc, self.adj_title)
        return uc

    def downcast(self):
        """
        Returns the "downcasted"[*]_ version of this model instance.
        
        .. [*]: In a multi-table model inheritance scenario, the term "downcasting"
                refers to the process to retrieve the child model instance given the 
                parent model instance.
        """
        # FIXME: this check is redundant, IMO (seldon)
        # if this method is called from a "concrete" instance
        # the lookup machinery either will return the instance itself
        # or a downcasted version of it (if any), which seems to me
        # the right behaviour for a ``downcast()`` method.
        if hasattr(
                self,
                'act_ptr'):  # method is being called from a subclass' instance
            return self
        cls = self.__class__  # ``self`` is an instance of the parent model
        for r in cls._meta.get_all_related_objects():
            if not issubclass(r.model, cls) or\
               not isinstance(r.field, models.OneToOneField):
                continue
            try:
                return getattr(self, r.get_accessor_name())
            except models.ObjectDoesNotExist:
                continue

    @property
    def attachments(self):
        return self.attachment_set.all()

    @property
    def transitions(self):
        return self.transition_set.all()

    @property
    def presenters(self):
        return self.presenter_set.all()

    @property
    def recipients(self):
        return self.recipient_set.all()

    @property
    def first_signers(self):
        return InstitutionCharge.objects.filter(
            actsupport__act__id=self.pk,
            actsupport__support_type=ActSupport.SUPPORT_TYPE.first_signer)

    @property
    def co_signers(self):
        return self.presenter_set.filter(
            actsupport__support_type=ActSupport.SUPPORT_TYPE.co_signer)

    @property
    def tags(self):
        return self.tag_set.all()

    @property
    def categories(self):
        return self.category_set.all()

    @property
    def locations(self):
        return self.location_set.all()

    @property
    def act_descriptors(self):
        """
        Returns the queryset of all those that modified the description
        """
        return self.actdescriptor_set.all()

    @property
    def content_type_id(self):
        """
        Returns id of the content type associated with this instance.
        """
        return ContentType.objects.get_for_model(self).id

    def status(self):
        """
        Returns the current status for the downcasted version of this act instance.
        
        Note: it seems that this method cannot be made into a property,
        since doing that raises a ``AttributeError: can't set attribute``
        exception during Django initialization. 
        """
        return self.downcast().status

    @property
    def related_news(self):
        return self.related_news_set.all()

    def get_transitions_groups(self):
        """
        retrieve a list of transitions grouped by status
        """
        groups = {}
        this = self.downcast()
        if not hasattr(this, 'STATUS'):
            return groups

        # initialize all status with an empty list of transitions
        for status in this.STATUS:
            groups[status[0]] = []

        # fill groups with ordered transitions
        for transition in this.transition_set.all().order_by(
                '-transition_date'):
            if groups.has_key(transition.final_status):
                groups.get(transition.final_status).append(transition)
        return groups

    def is_final_status(self, status):
        this = self.downcast()

        if not hasattr(this, 'FINAL_STATUSES'):
            return False

        for final_status in this.FINAL_STATUSES:
            if status == final_status[0]:
                return True

        return False

    def get_last_transition(self):
        if self.transitions:
            return list(self.transitions)[-1]
        return False
Ejemplo n.º 26
0
class EthereumToken(models.Model):
    NULL_ADDRESS = "0x0000000000000000000000000000000000000000"
    chain = models.ForeignKey(Chain,
                              on_delete=models.CASCADE,
                              related_name="tokens")
    code = models.CharField(max_length=8)
    name = models.CharField(max_length=500)
    decimals = models.PositiveIntegerField(default=18)
    address = EthereumAddressField(default=NULL_ADDRESS)

    objects = models.Manager()
    ERC20tokens = QueryManager(~Q(address=NULL_ADDRESS))
    ethereum = QueryManager(address=NULL_ADDRESS)

    @property
    def is_ERC20(self) -> bool:
        return self.address != self.NULL_ADDRESS

    def __str__(self) -> str:
        components = [self.code]
        if self.is_ERC20:
            components.append(self.address)

        components.append(str(self.chain_id))
        return " - ".join(components)

    def get_contract(self, w3: Web3) -> Contract:
        if not self.is_ERC20:
            raise ValueError("Not an ERC20 token")

        return w3.eth.contract(abi=EIP20_ABI, address=self.address)

    def build_transfer_transaction(self, w3: Web3, sender, recipient,
                                   amount: EthereumTokenAmount):

        chain_id = int(w3.net.version)
        message = f"Web3 client is on network {chain_id}, token {self.code} is on {self.chain_id}"
        assert self.chain_id == chain_id, message

        transaction_params = {
            "chainId": chain_id,
            "nonce": w3.eth.getTransactionCount(sender),
            "gasPrice": w3.eth.generateGasPrice(),
            "gas": TRANSFER_GAS_LIMIT,
            "from": sender,
        }

        if self.is_ERC20:
            transaction_params.update({
                "to":
                self.address,
                "value":
                0,
                "data":
                encode_transfer_data(recipient, amount)
            })
        else:
            transaction_params.update({
                "to": recipient,
                "value": amount.as_wei
            })
        return transaction_params

    def _decode_transaction_data(self,
                                 tx_data,
                                 contract: Optional[Contract] = None) -> Tuple:
        if not self.is_ERC20:
            return tx_data.to, self.from_wei(tx_data.value)

        try:
            assert tx_data[
                "to"] == self.address, f"Not a {self.code} transaction"
            assert contract is not None, f"{self.code} contract interface required to decode tx"

            fn, args = contract.decode_function_input(tx_data.input)

            # TODO: is this really the best way to identify the transaction as a value transfer?
            transfer_idenfifier = contract.functions.transfer.function_identifier
            assert transfer_idenfifier == fn.function_identifier, "No transfer transaction"

            return args["_to"], self.from_wei(args["_value"])
        except AssertionError as exc:
            logger.warning(exc)
            return None, None
        except Exception as exc:
            logger.warning(exc)
            return None, None

    def _decode_transaction(self, transaction: Transaction) -> Tuple:
        # A transfer transaction input is 'function,address,uint256'
        # i.e, 16 bytes + 20 bytes + 32 bytes = hex string of length 136
        try:
            # transaction input strings are '0x', so we they should be 138 chars long
            assert len(
                transaction.data) == 138, "Not a ERC20 transfer transaction"
            assert transaction.logs.count(
            ) == 1, "Transaction does not contain log changes"

            recipient_address = to_checksum_address(transaction.data[-104:-64])

            wei_transferred = int(transaction.data[-64:], 16)
            tx_log = transaction.logs.first()

            assert int(
                tx_log.data,
                16) == wei_transferred, "Log data and tx amount do not match"

            return recipient_address, self.from_wei(wei_transferred)
        except AssertionError as exc:
            logger.info(f"Failed to get transfer data from transaction: {exc}")
            return None, None
        except ValueError:
            logger.info(
                f"Failed to extract transfer amounts from {transaction.hash.hex()}"
            )
            return None, None
        except Exception as exc:
            logger.exception(exc)
            return None, None

    def from_wei(self, wei_amount: int) -> EthereumTokenAmount:
        value = Decimal(wei_amount) / (10**self.decimals)
        return EthereumTokenAmount(amount=value, currency=self)

    @staticmethod
    def ETH(chain: Chain):
        eth, _ = EthereumToken.objects.get_or_create(
            chain=chain, code="ETH", defaults={"name": "Ethereum"})
        return eth

    @classmethod
    def make(cls, address: str, chain: Chain, **defaults):
        if address == EthereumToken.NULL_ADDRESS:
            return EthereumToken.ETH(chain)

        obj, _ = cls.objects.update_or_create(address=address,
                                              chain=chain,
                                              defaults=defaults)
        return obj

    class Meta:
        unique_together = (("chain", "address"), )
Ejemplo n.º 27
0
class Wallet(TimeStampedModel, SoftDeletableModel):
    bank = models.OneToOneField('wallet.Bank',
                                related_name='wallet',
                                unique=True,
                                on_delete=models.PROTECT)

    objects = SoftDeletableManager()
    removed = QueryManager(is_removed=True)

    class Meta:
        ordering = ['bank']
        permissions = (('can_view_wallet_report', 'Can view wallet report'), )

    @staticmethod
    def validate_amount(amount):
        if not isinstance(amount, (int, Decimal, str)):
            raise ValueError('Amount need to be a string, integer or Decimal.')
        return Decimal(amount)

    def register_income(self, amount, reference):
        amount = self.validate_amount(amount)
        if amount <= Decimal('0'):
            raise ValueError('Amount need to be positive number, not %s' %
                             amount)

        return self._create_transaction(value, reference)

    def _create_transaction(self, amount, reference):
        return Transaction.objects.create(wallet=self,
                                          reference=reference,
                                          amount=amount)

    def register_expense(self, amount, reference, current_lock=None):
        amount = self.validate_amount(amount)
        if amount >= Decimal('0'):
            raise ValueError('Amount need to be negative number, not %s' %
                             amount)

        if current_lock is not None:
            if current_lock.wallet != self:
                raise ValueError('Lock not for this wallet!')

            if self.balance >= amount:
                return self._create_transaction(amount, reference)
            else:
                raise WalletHasInsufficientFunds(
                    'Not insufficient funds to spend %s' % amount)

        with Locked(self):
            if self.balance >= amount:
                return self._create_transaction(amount, reference)
            else:
                raise WalletHasInsufficientFunds(
                    'Not insufficient funds to spend %s' % amount)

    @property
    def balance(self):
        return self.transactions.aggregate(
            Sum('transaction_amount'))['transaction_amount__sum']

    def __str__(self):
        return '({})'.format(self.bank)
Ejemplo n.º 28
0
class Project(OrderedModel):
    """
    A project completed by the agency.
    """
    STATUS = Choices(
        ('draft', _('Draft')),
        ('published', _('Published'))
    )
    name = models.CharField(
        max_length=200,
        help_text='Limited to 200 characters.',
        default=''
    )
    slug = models.SlugField(
        help_text='Used to build the project URL. \
            Will populate from the name field.',
        default='',
        unique=True
    )
    description = models.TextField(
        default=''
    )
    categories = models.ManyToManyField(
        Category,
        blank=True,
        default='',
        help_text='Optional. Used for administrative purposes only. Not \
            shown to the public.'
    )
    hero_image = ImageField(
        upload_to='projects/hero_images',
        default='',
        help_text='Please use jpg (jpeg) or png files only. Will be resized \
            for public display.',
        validators=[validate_file_type]
    )
    is_featured = models.BooleanField(
        default=False,
        help_text='Check this box to feature this project on the homepage \
            and project list page.',
    )
    status = StatusField(default='draft')

    objects = models.Manager()
    published = QueryManager(status='published')
    featured = QueryManager(Q(is_featured=True) & Q(status='published'))

    class Meta(OrderedModel.Meta):
        pass

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('project_detail', kwargs={'slug': self.slug})

    def get_next(self):
        """
        Method to return the next object by order field.
        """
        next = self.__class__.objects.filter(order__gt=self.order)
        try:
            return next[0].get_absolute_url()
        except IndexError:
            return False

    def get_previous(self):
        """
        Method to return the previous object by order field.
        """
        previous = self.__class__.objects.filter(order__lt=self.order)
        try:
            return previous[0].get_absolute_url()
        except IndexError:
            return False
Ejemplo n.º 29
0
class Attribute(models.Model):
    label = models.CharField(verbose_name=_('label'),
                             max_length=63,
                             unique=True)
    description = models.TextField(verbose_name=_('description'), blank=True)
    name = models.SlugField(verbose_name=_('name'),
                            max_length=256,
                            unique=True)
    required = models.BooleanField(verbose_name=_('required'),
                                   blank=True,
                                   default=False)
    asked_on_registration = models.BooleanField(
        verbose_name=_('asked on registration'), blank=True, default=False)
    user_editable = models.BooleanField(verbose_name=_('user editable'),
                                        blank=True,
                                        default=False)
    user_visible = models.BooleanField(verbose_name=_('user visible'),
                                       blank=True,
                                       default=False)
    multiple = models.BooleanField(verbose_name=_('multiple'),
                                   blank=True,
                                   default=False)
    kind = models.CharField(max_length=16, verbose_name=_('kind'))
    disabled = models.BooleanField(verbose_name=_('disabled'),
                                   blank=True,
                                   default=False)
    searchable = models.BooleanField(verbose_name=_('searchable'),
                                     blank=True,
                                     default=False)

    scopes = models.CharField(verbose_name=_('scopes'),
                              help_text=_('scopes separated by spaces'),
                              blank=True,
                              default='',
                              max_length=256)

    order = models.PositiveIntegerField(verbose_name=_('order'), default=0)

    objects = managers.AttributeManager(disabled=False)
    all_objects = managers.AttributeManager()

    registration_attributes = QueryManager(asked_on_registration=True)
    user_attributes = QueryManager(user_editable=True)

    def get_form_field(self, **kwargs):
        from . import attribute_kinds
        kwargs['label'] = self.label
        kwargs['required'] = self.required
        if self.description:
            kwargs['help_text'] = self.description
        return attribute_kinds.get_form_field(self.kind, **kwargs)

    def get_kind(self):
        from . import attribute_kinds
        return attribute_kinds.get_kind(self.kind)

    def contribute_to_form(self, form, **kwargs):
        form.fields[self.name] = self.get_form_field(**kwargs)

    def get_value(self, owner, verified=None):
        kind = self.get_kind()
        deserialize = kind['deserialize']
        atvs = AttributeValue.objects.with_owner(owner)
        if verified is True or verified is False:
            atvs = atvs.filter(verified=verified)
        if self.multiple:
            result = []
            for atv in atvs.filter(attribute=self, multiple=True):
                result.append(deserialize(atv.content))
            return result
        else:
            try:
                atv = atvs.get(attribute=self, multiple=False)
                return deserialize(atv.content)
            except AttributeValue.DoesNotExist:
                return kind['default']

    def set_value(self, owner, value, verified=False):
        serialize = self.get_kind()['serialize']
        # setting to None is to delete
        if value is None:
            AttributeValue.objects.with_owner(owner).filter(
                attribute=self).delete()
            return

        if self.multiple:
            assert isinstance(value, (list, set, tuple))
            values = value
            for value in values:
                content = serialize(value)
                av, created = AttributeValue.objects.get_or_create(
                    content_type=ContentType.objects.get_for_model(owner),
                    object_id=owner.pk,
                    attribute=self,
                    multiple=True,
                    content=content,
                    defaults={'verified': verified})
                if not created:
                    av.verified = verified
                    av.save()
        else:
            content = serialize(value)
            av, created = AttributeValue.objects.get_or_create(
                content_type=ContentType.objects.get_for_model(owner),
                object_id=owner.pk,
                attribute=self,
                multiple=False,
                defaults={
                    'content': content,
                    'verified': verified
                })
            if not created:
                av.content = content
                av.verified = verified
                av.save()

        # if owner has a modified field, update it
        try:
            modified = owner.__class__._meta.get_field('modified')
        except FieldDoesNotExist:
            pass
        else:
            if getattr(modified, 'auto_now', False):
                owner.save(update_fields=['modified'])

    def natural_key(self):
        return (self.name, )

    def __unicode__(self):
        return self.label

    class Meta:
        verbose_name = _('attribute definition')
        verbose_name_plural = _('attribute definitions')
        ordering = ('order', 'id')
Ejemplo n.º 30
0
class Votation(models.Model):
    """
    This model stores information about a single ballot. A ballot has
    an outcome determined by the sum of the votes of the participants
    and the legal number of the ballot itself. The latter depend on
    the type of ballot.
    """
    OUTCOMES = Choices(
        (0, 'No Esito'),
        (1, 'Approvato'),
        (2, 'Respinto'),
        (3, 'SI Numero Legale'),
        (4, 'NO Numero Legale'),
        (5, 'Annullata'),
    )

    idnum = models.CharField(blank=True, max_length=64)
    sitting = models.ForeignKey(Sitting, blank=False, null=False)
    act = models.ForeignKey(Act, null=True)

    # this field is used to keep the textual description of the related act
    # as expressed in the voting system
    act_descr = models.CharField(blank=True, max_length=255)

    group_set = models.ManyToManyField(Group, through='GroupVote')
    charge_set = models.ManyToManyField(InstitutionCharge,
                                        through='ChargeVote')
    n_legal = models.IntegerField(default=0)
    n_presents = models.IntegerField(default=0)
    n_partecipants = models.IntegerField(default=0)
    n_absents = models.IntegerField(default=0)
    n_yes = models.IntegerField(default=0)
    n_no = models.IntegerField(default=0)
    n_abst = models.IntegerField(default=0)
    n_maj = models.IntegerField(default=0)
    outcome = models.IntegerField(choices=OUTCOMES, blank=True, null=True)
    is_key = models.BooleanField(
        default=False, help_text=_("Specify whether this is a key votation"))
    n_rebels = models.IntegerField(default=0)
    slug = models.SlugField(max_length=500, blank=True, null=True)
    datetime = models.DateTimeField(blank=True,
                                    null=True,
                                    verbose_name=_("datetime"))
    source_url = models.URLField(
        max_length=500,
        blank=True,
        null=True,
        help_text=_(
            "If the object has been imported from a public URL, report it here"
        ))

    # default manager must be explicitly defined, when
    # at least another manager is present
    objects = models.Manager()

    # use this manager to retrieve only key votations
    key = QueryManager(is_key=True).order_by('-sitting__date')

    # use this manager to retrieve only linked acts
    is_linked_to_act = QueryManager(act__isnull=False)

    # activation of the ``is_linked_filter``
    # add ``act`` to the ``list_filter`` list in ``admin.py``
    # to filter votations based on the existence of a related act
    act.is_linked_filter = True

    @property
    def is_key_yesno(self):
        if self.is_key:
            return _('yes')
        else:
            return _('no')

    def save(self, *args, **kwargs):
        """
        This method takes care of setting a default slug for Votations
        that are linked to a Sitting. 

        This transparently helps the slug field for most of the "normal"
        use cases of Votation.
        """
        if not self.slug:
            self.slug = self.get_default_slug()

        super(Votation, self).save(*args, **kwargs)

    @property
    def date(self):
        return self.sitting.date

    def get_default_slug(self):
        """
        This method will be used for assigning a default slug to a
        Votation that does not have one.
        """

        if self.sitting and self.idnum:
            cleaned_idnum = re.sub(r'[^\w\d]+', '-', self.idnum)
            slug = slugify("%s-%s" %
                           (self.sitting.date.isoformat(), cleaned_idnum))
            return slug[:100]
        else:
            raise ValueError(
                "In order to compute the default slug, the Votation should be linked to a Sitting"
            )

    class Meta:
        verbose_name = _('votation')
        verbose_name_plural = _('votations')

        unique_together = (('slug', ), (
            'sitting',
            'idnum',
        ))

    def __unicode__(self):

        return _('Votation %(idnum)s') % {
            "idnum": self.idnum,
        }

    @models.permalink
    def get_absolute_url(self):
        """
        Introduce url based on slugs. To keen retro-compatibility during 
        introduction of slugs, it also allows to view the old url using pk.
        """

        if getattr(self, "slug", None) and self.slug:
            return ("om_votation_detail", (), {'slug': self.slug})
        else:
            return ("om_votation_detail", (), {'pk': self.pk})

    @property
    def group_votes(self):
        return self.groupvote_set.all()

    @property
    def charge_votes(self):
        return self.chargevote_set.all()

    @property
    def charge_rebel_votes(self):
        return self.chargevote_set.filter(is_rebel=True)

    @property
    def transitions(self):
        return self.transition_set.all()

    @property
    def ref_act(self):
        act = None
        if self.act:
            act = self.act


#        elif self.transitions.count() > 0:
        else:
            try:
                act = self.transitions[0].act
            except IndexError:
                # self.transitions is empty
                pass

        return act

    @property
    def is_linked(self):
        return self.ref_act is not None

    @property
    def is_secret(self):
        """
        A votation is secret if there is at least one voter with a secret
        vote (there could be voters that casted differnt types of votes, 
        e.g. absent or abstained)
        """
        return self.charge_votes.filter(
            vote=ChargeVote.VOTES.secret).count() > 0

    @property
    def charges_count(self):
        return self.chargevote_set.filter(charge__can_vote=True).count()

    def update_presence_caches(self):
        """
        update presence caches for each voting charge of this votation
        """
        for vc in self.charge_votes:
            vc.charge.update_presence_cache()

    def verify_integrity(self):
        """
        Verify the integrity of the ballot structure. In particular checks that 
        related self.votes are consistent with the votes counted in the self.n_*
        fields. If an error is detected, raise an exception explaining the problem
        """
        errors = []

        # check legal number is greater than 0
        if self.n_legal < 0:
            errors.append(
                "The legal number should always be positive. Passed: %s" %
                self.n_legal)

        # count the number of presents is consistent with yes + no + abstained

        n_sum = self.n_yes + self.n_no + self.n_abst
        if n_sum > self.n_presents:
            errors.append(
                "The number of presents (%s) is smaller than the sum of yes (%s), no (%s) and abstained (%s): %s. Additional info: absents = %s, rebels = %s"
                % (self.n_presents, self.n_yes, self.n_no, self.n_abst, n_sum,
                   self.n_absents, self.n_rebels))

        num_charge_votes = self.charge_votes.count()
        if num_charge_votes < self.n_presents:
            errors.append(
                "The related votes (%s) are less that the reported number of presents (%s)"
                % (
                    num_charge_votes,
                    self.n_presents,
                ))

        if num_charge_votes < n_sum:
            errors.append(
                "The number of related votes (%s) is less than the yes, no and abst votes (%s)"
                % (
                    num_charge_votes,
                    n_sum,
                ))

        # check the number of presents is greater than the legal number

        if self.n_presents < self.n_legal:
            errors.append(
                "Number of presents (%s) should not be less than legal number (%s)"
                % (self.n_presents, self.n_legal))

        if len(errors) > 0:
            raise Exception(",".join(errors))

    @property
    def majority_vs_minority(self):

        count = {'majority': Counter(), 'minority': Counter()}

        for cv in self.chargevote_set.all():

            if (not cv.charge_group_at_vote_date
                    or cv.charge_group_at_vote_date.is_majority_now):
                count['majority'].update({cv.vote: 1})
            else:
                count['minority'].update({cv.vote: 1})

        return dict(count)