Exemplo n.º 1
0
class Nonce(UUIDModelMixin):
    """
    Stores temporary nonce values used for cryptographic handshakes during syncing.
    These nonces are requested by the client, and then generated and stored by the server.
    When the client then goes to initiate a sync session, it signs the nonce value using
    the private key from the certificate it is using for the session, to prove to the
    server that it owns the certificate. The server checks that the nonce exists and hasn't
    expired, and then deletes it.
    """

    uuid_input_fields = "RANDOM"

    timestamp = models.DateTimeField(default=timezone.now)
    ip = models.CharField(max_length=100, blank=True)

    @classmethod
    def use_nonce(cls, nonce_value):
        with transaction.atomic():
            # try fetching the nonce
            try:
                nonce = cls.objects.get(id=nonce_value)
            except cls.DoesNotExist:
                raise NonceDoesNotExist()
            # check that the nonce hasn't expired
            if not (0 <
                    (timezone.now() - nonce.timestamp).total_seconds() < 60):
                nonce.delete()
                raise NonceExpired()
            # now that we've used it, delete the nonce
            nonce.delete()
Exemplo n.º 2
0
class StaticPage(models.Model):
    ABOUT_US = 0
    FOOTER = 1
    HOMEPAGE = 2
    LESSONS_LEARNED = 3

    PAGES = (
      (ABOUT_US, "About us"),
      (FOOTER, "Footer"),
      (HOMEPAGE, "Homepage"),
      (LESSONS_LEARNED, "Lessons learned")
    )

    text = HTMLField()
    last_modified = models.DateTimeField(auto_now=True)
    page = models.IntegerField(
        choices=PAGES,
        blank=False,
        null=False,
        default=FOOTER,
        help_text="Page location"
    )

    class Meta:
        ordering = ["page", "last_modified"]
        verbose_name = "Static page"
        verbose_name_plural = "Static pages"

    def __str__(self):
        return self.get_page_display()
Exemplo n.º 3
0
class Bid(mptt.models.MPTTModel):
    objects = BidManager()
    event = models.ForeignKey(
        'Event',
        on_delete=models.PROTECT,
        verbose_name='Event',
        null=True,
        blank=True,
        related_name='bids',
        help_text='Required for top level bids if Run is not set',
    )
    speedrun = models.ForeignKey(
        'SpeedRun',
        on_delete=models.PROTECT,
        verbose_name='Run',
        null=True,
        blank=True,
        related_name='bids',
    )
    parent = mptt.models.TreeForeignKey(
        'self',
        on_delete=models.PROTECT,
        verbose_name='Parent',
        editable=False,
        null=True,
        blank=True,
        related_name='options',
    )
    name = models.CharField(max_length=64)
    state = models.CharField(
        max_length=32,
        db_index=True,
        default='OPENED',
        choices=(
            ('PENDING', 'Pending'),
            ('DENIED', 'Denied'),
            ('HIDDEN', 'Hidden'),
            ('OPENED', 'Opened'),
            ('CLOSED', 'Closed'),
        ),
    )
    description = models.TextField(max_length=1024, blank=True)
    shortdescription = models.TextField(
        max_length=256,
        blank=True,
        verbose_name='Short Description',
        help_text='Alternative description text to display in tight spaces',
    )
    goal = models.DecimalField(decimal_places=2,
                               max_digits=20,
                               null=True,
                               blank=True,
                               default=None)
    istarget = models.BooleanField(
        default=False,
        verbose_name='Target',
        help_text=
        "Set this if this bid is a 'target' for donations (bottom level choice or challenge)",
    )
    allowuseroptions = models.BooleanField(
        default=False,
        verbose_name='Allow User Options',
        help_text=
        'If set, this will allow donors to specify their own options on the donate page (pending moderator approval)',
    )
    option_max_length = models.PositiveSmallIntegerField(
        'Max length of user suggestions',
        blank=True,
        null=True,
        default=None,
        validators=[MinValueValidator(1),
                    MaxValueValidator(64)],
        help_text=
        'If allowuseroptions is set, this sets the maximum length of user-submitted bid suggestions',
    )
    revealedtime = models.DateTimeField(verbose_name='Revealed Time',
                                        null=True,
                                        blank=True)
    biddependency = models.ForeignKey(
        'self',
        on_delete=models.PROTECT,
        verbose_name='Dependency',
        null=True,
        blank=True,
        related_name='dependent_bids',
    )
    total = models.DecimalField(decimal_places=2,
                                max_digits=20,
                                editable=False,
                                default=Decimal('0.00'))
    count = models.IntegerField(editable=False)

    class Meta:
        app_label = 'tracker'
        unique_together = ((
            'event',
            'name',
            'speedrun',
            'parent',
        ), )
        ordering = [
            'event__datetime', 'speedrun__starttime', 'parent__name', 'name'
        ]
        permissions = (
            ('top_level_bid', 'Can create new top level bids'),
            ('delete_all_bids', 'Can delete bids with donations attached'),
            ('view_hidden_bid', 'Can view hidden bids'),
        )

    class MPTTMeta:
        order_insertion_by = ['name']

    def get_absolute_url(self):
        return reverse('tracker:bid', args=(self.id, ))

    def natural_key(self):
        if self.parent:
            return (
                self.event.natural_key(),
                self.name,
                self.speedrun.natural_key() if self.speedrun else None,
                self.parent.natural_key(),
            )
        elif self.speedrun:
            return (self.event.natural_key(), self.name,
                    self.speedrun.natural_key())
        else:
            return (self.event.natural_key(), self.name)

    def clean(self):
        # Manually de-normalize speedrun/event/state to help with searching
        # TODO: refactor this logic, it should be correct, but is probably not minimal

        if self.option_max_length:
            if not self.allowuseroptions:
                raise ValidationError(
                    _('Cannot set option_max_length without allowuseroptions'),
                    code='invalid',
                )
                # FIXME: why is this printing 'please enter a whole number'?
                # raise ValidationError(
                #     {
                #         'option_max_length': ValidationError(
                #             _('Cannot set option_max_length without allowuseroptions'),
                #             code='invalid',
                #         ),
                #     }
                # )
            if self.pk:
                for child in self.get_children():
                    if len(child.name) > self.option_max_length:
                        raise ValidationError(
                            _('Cannot set option_max_length to %(length)d, child name `%(name)s` is too long'
                              ),
                            code='invalid',
                            params={
                                'length': self.option_max_length,
                                'name': child.name,
                            },
                        )
                        # TODO: why is this printing 'please enter a whole number'?
                        # raise ValidationError({
                        #     'option_max_length': ValidationError(
                        #         _('Cannot set option_max_length to %(length), child name %(name) is too long'),
                        #         code='invalid',
                        #         params={
                        #             'length': self.option_max_length,
                        #             'name': child.name,
                        #         }
                        #     ),
                        # })

        if self.parent:
            max_len = self.parent.option_max_length
            if max_len and len(self.name) > max_len:
                raise ValidationError({
                    'name':
                    ValidationError(
                        _('Name is longer than %(limit)s characters'),
                        params={'limit': max_len},
                        code='invalid',
                    ),
                })
        if self.biddependency:
            if self.parent or self.speedrun:
                if self.event != self.biddependency.event:
                    raise ValidationError(
                        'Dependent bids must be on the same event')
        if not self.parent:
            if not self.get_event():
                raise ValidationError(
                    'Top level bids must have their event set')
        if not self.goal:
            self.goal = None
        elif self.goal <= Decimal('0.0'):
            raise ValidationError('Goal should be a positive value')
        if self.state in ['PENDING', 'DENIED'
                          ] and (not self.istarget or not self.parent
                                 or not self.parent.allowuseroptions):
            raise ValidationError({
                'state':
                f'State `{self.state}` can only be set on targets with parents that allow user options'
            })
        if self.istarget and self.options.count() != 0:
            raise ValidationError('Targets cannot have children')
        if self.parent and self.parent.istarget:
            raise ValidationError('Cannot set that parent, parent is a target')
        if self.istarget and self.allowuseroptions:
            raise ValidationError(
                'A bid target cannot allow user options, since it cannot have children.'
            )
        if (not self.allowuseroptions and self.pk and
                self.get_children().filter(state__in=['PENDING', 'DENIED'])):
            raise ValidationError({
                'allowuseroptions':
                'Bid has pending/denied children, cannot remove allowing user options'
            })
        same_name = Bid.objects.filter(
            speedrun=self.speedrun,
            event=self.event,
            parent=self.parent,
            name__iexact=self.name,
        ).exclude(pk=self.pk)
        if same_name.exists():
            raise ValidationError(
                'Cannot have a bid under the same event/run/parent with the same name'
            )

    def save(self, *args, skip_parent=False, **kwargs):
        if self.parent:
            self.check_parent()
        if self.speedrun:
            self.event = self.speedrun.event
        if self.state in ['OPENED', 'CLOSED'] and not self.revealedtime:
            self.revealedtime = datetime.utcnow().replace(tzinfo=pytz.utc)
        if self.biddependency:
            self.event = self.biddependency.event
            if not self.speedrun:
                self.speedrun = self.biddependency.speedrun
        self.update_total()
        super(Bid, self).save(*args, **kwargs)
        if self.pk:
            for option in self.get_descendants():
                if option.check_parent():
                    option.save(skip_parent=True)
        if self.parent and not skip_parent:
            self.parent.save()

    def check_parent(self):
        changed = False
        if self.speedrun != self.parent.speedrun:
            self.speedrun = self.parent.speedrun
            changed = True
        if self.event != self.parent.event:
            self.event = self.parent.event
            changed = True
        if self.state not in ['PENDING', 'DENIED'
                              ] and self.state != self.parent.state:
            self.state = self.parent.state
            changed = True
        return changed

    @property
    def has_options(self):
        return self.allowuseroptions or self.public_options.exists()

    @property
    def public_options(self):
        return self.options.filter(Q(state='OPENED')
                                   | Q(state='CLOSED')).order_by('-total')

    def update_total(self):
        if self.istarget:
            self.total = self.bids.filter(
                donation__transactionstate='COMPLETED').aggregate(
                    Sum('amount'))['amount__sum'] or Decimal('0.00')
            self.count = self.bids.filter(
                donation__transactionstate='COMPLETED').count()
            # auto close this if it's a challenge with no children and the goal's been met
            if (self.goal and self.state == 'OPENED'
                    and self.total >= self.goal and self.istarget):
                self.state = 'CLOSED'
        else:
            options = self.options.exclude(state__in=('DENIED',
                                                      'PENDING')).aggregate(
                                                          Sum('total'),
                                                          Sum('count'))
            self.total = options['total__sum'] or Decimal('0.00')
            self.count = options['count__sum'] or 0

    def get_event(self):
        if self.speedrun:
            return self.speedrun.event
        else:
            return self.event

    def full_label(self, addMoney=True):
        result = [self.fullname()]
        if self.speedrun:
            result = [self.speedrun.name_with_category(), ' : '] + result
        if addMoney:
            result += [' $', '%0.2f' % self.total]
            if self.goal:
                result += [' / ', '%0.2f' % self.goal]
        return ''.join(result)

    def __str__(self):
        if self.parent:
            return f'{self.parent} (Parent) -- {self.name}'
        elif self.speedrun:
            return f'{self.speedrun.name_with_category()} (Run) -- {self.name}'
        else:
            return f'{self.event} (Event) -- {self.name}'

    def fullname(self):
        parent = self.parent.fullname() + ' -- ' if self.parent else ''
        return parent + self.name
Exemplo n.º 4
0
class Legislation(_TaxonomyModel):
    title = models.CharField(max_length=256)
    abstract = models.CharField(max_length=1024, blank=True, null=True)
    country = models.ForeignKey(Country, related_name="legislations")
    language = models.CharField(choices=constants.ALL_LANGUAGES,
                                default=constants.DEFAULT_LANGUAGE_VALUE,
                                max_length=64)
    law_type = models.CharField(choices=constants.LEGISLATION_TYPE,
                                default=constants.LEGISLATION_DEFAULT_VALUE,
                                max_length=64)
    year = models.IntegerField(default=constants.LEGISLATION_YEAR_RANGE[-1])
    year_amendment = models.IntegerField(
        default=constants.LEGISLATION_DEFAULT_YEAR, blank=True, null=True)
    year_mention = models.CharField(max_length=1024, blank=True, null=True)
    geo_coverage = models.CharField(
        choices=constants.GEOGRAPHICAL_COVERAGE,
        default=constants.GEOGRAPHICAL_COVERAGE_DEFAULT_VALUE,
        max_length=64,
        null=True)
    source = models.CharField(max_length=256, blank=True, null=True)
    source_type = models.CharField(choices=constants.SOURCE_TYPE,
                                   default=constants.SOURCE_TYPE_DEFAULT_VALUE,
                                   max_length=64,
                                   blank=True,
                                   null=True)
    website = models.URLField(max_length=2000, blank=True, null=True)
    legispro_article = models.CharField(max_length=512, blank=True, null=True)
    import_from_legispro = models.BooleanField(default=False)
    date_created = models.DateTimeField(auto_now_add=True)
    date_updated = models.DateTimeField(auto_now=True)
    pdf_file = models.FileField(null=True, blank=True)
    pdf_file_name = models.CharField(null=True, max_length=256)

    objects = LegislationManager()

    @property
    def country_name(self):
        return self.country.name

    @property
    def country_iso(self):
        return self.country.iso

    @property
    def other_legislations(self):
        other = {}
        for classification in self.classifications.all():
            other[classification] = Legislation.objects.filter(
                classifications__id__exact=classification.pk).exclude(
                    pk=self.pk).all()[:3]
        return other

    # @TODO: Change the __str__ to something more appropriate
    def __str__(self):
        return "Legislation: " + ' | '.join([self.country.name, self.law_type])

    def highlighted_title(self):
        """
        If this law was returned as a result of an elasticsearch query, return
        the title with the search terms highlighted. If not, return the original
        title.
        """
        return getattr(self, '_highlighted_title', self.title)

    def highlighted_abstract(self):
        """
        If this law was returned as a result of an elasticsearch query, return
        the abstract with the search terms highlighted. If not, return an empty
        string.
        """
        return getattr(self, '_highlighted_abstract', '')

    def highlighted_pdf_text(self):
        """
        If this law was returned as a result of an elasticsearch query, return
        the pdf_text with the search terms highlighted. If not, return an empty
        string.
        """
        return getattr(self, '_highlighted_pdf_text', '')

    def highlighted_classifications(self):
        """
        If this law was returned as a result of an elasticsearch query, return
        a list of classification names with the search terms highlighted. If
        not, return the original list of classification names.
        """
        return getattr(
            self, '_highlighted_classifications',
            self.classifications.all().values_list('name', flat=True))

    def highlighted_tags(self):
        """
        If this law was returned as a result of an elasticsearch query, return
        a list of tag names with the search terms highlighted. If not, return
        the original list of tag names.
        """
        return getattr(self, '_highlighted_tags',
                       self.tags.all().values_list('name', flat=True))

    def highlighted_articles(self):
        """
        If this law was returned as a result of an elasticsearch query, return
        a list of dictionaries representing articles with the search terms
        highlighted in the text field. If not, return an empty list.
        """
        return getattr(self, '_highlighted_articles', [])

    def save_pdf_pages(self):
        if settings.DEBUG:
            time_to_load_pdf = time.time()
        if settings.DEBUG:
            print("INFO: FS pdf file load time: %fs" %
                  (time.time() - time_to_load_pdf))
            time_begin_transaction = time.time()

        with transaction.atomic():
            pdf = pdftotext.PDF(self.pdf_file)
            for idx, page in enumerate(pdf):
                page = page.replace('\x00', '')
                LegislationPage(page_text="<pre>%s</pre>" % page,
                                page_number=idx + 1,
                                legislation=self).save()

        if settings.DEBUG:
            print("INFO: ORM models.LegislationPages save time: %fs" %
                  (time.time() - time_begin_transaction))

        # This is necessary in order to trigger the signal that will update the
        # ElasticSearch index.
        self.save()
Exemplo n.º 5
0
class Bid(mptt.models.MPTTModel):
    objects = BidManager()
    event = models.ForeignKey('Event', on_delete=models.PROTECT, verbose_name='Event', null=True,
                              blank=True, related_name='bids', help_text='Required for top level bids if Run is not set')
    speedrun = models.ForeignKey('SpeedRun', on_delete=models.PROTECT,
                                 verbose_name='Run', null=True, blank=True, related_name='bids')
    parent = mptt.models.TreeForeignKey('self', on_delete=models.PROTECT, verbose_name='Parent',
                                        editable=False, null=True, blank=True, related_name='options')
    name = models.CharField(max_length=64)
    state = models.CharField(max_length=32, choices=(('PENDING', 'Pending'), ('DENIED', 'Denied'), (
        'HIDDEN', 'Hidden'), ('OPENED', 'Opened'), ('CLOSED', 'Closed')), default='OPENED')
    description = models.TextField(max_length=1024, blank=True)
    shortdescription = models.TextField(max_length=256, blank=True, verbose_name='Short Description',
                                        help_text="Alternative description text to display in tight spaces")
    goal = models.DecimalField(
        decimal_places=2, max_digits=20, null=True, blank=True, default=None)
    istarget = models.BooleanField(default=False, verbose_name='Target',
                                   help_text="Set this if this bid is a 'target' for donations (bottom level choice or challenge)")
    allowuseroptions = models.BooleanField(default=False, verbose_name="Allow User Options",
                                           help_text="If set, this will allow donors to specify their own options on the donate page (pending moderator approval)")
    revealedtime = models.DateTimeField(
        verbose_name='Revealed Time', null=True, blank=True)
    biddependency = models.ForeignKey('self', on_delete=models.PROTECT,
                                      verbose_name='Dependency', null=True, blank=True, related_name='dependent_bids')
    total = models.DecimalField(
        decimal_places=2, max_digits=20, editable=False, default=Decimal('0.00'))
    count = models.IntegerField(editable=False)

    class Meta:
        app_label = 'tracker'
        unique_together = (('event', 'name', 'speedrun', 'parent',),)
        ordering = ['event__datetime',
                    'speedrun__starttime', 'parent__name', 'name']
        permissions = (
            ('top_level_bid', 'Can create new top level bids'),
            ('delete_all_bids', 'Can delete bids with donations attached'),
            ('view_hidden', 'Can view hidden bids'),
        )

    class MPTTMeta:
        order_insertion_by = ['name']

    def natural_key(self):
        if self.parent:
            return (self.event.natural_key(), self.name, self.speedrun.natural_key() if self.speedrun else None, self.parent.natural_key())
        elif self.speedrun:
            return (self.event.natural_key(), self.name, self.speedrun.natural_key())
        else:
            return (self.event.natural_key(), self.name)

    def clean(self):
        # Manually de-normalize speedrun/event/state to help with searching
        # TODO: refactor this logic, it should be correct, but is probably not minimal
        if self.speedrun:
            self.event = self.speedrun.event
        if self.parent:
            curr = self.parent
            while curr.parent != None:
                curr = curr.parent
            root = curr
            self.speedrun = root.speedrun
            self.event = root.event
            if self.state != 'PENDING' and self.state != 'DENIED':
                self.state = root.state
        if self.biddependency:
            if self.parent or self.speedrun:
                if self.event != self.biddependency.event:
                    raise ValidationError(
                        'Dependent bids must be on the same event')
            self.event = self.biddependency.event
            if not self.speedrun:
                self.speedrun = self.biddependency.speedrun
        if not self.parent:
            if not self.get_event():
                raise ValidationError(
                    'Top level bids must have their event set')
        if self.id:
            for option in self.get_descendants():
                option.speedrun = self.speedrun
                option.event = self.event
                if option.state != 'PENDING' and option.state != 'DENIED':
                    option.state = self.state
                option.save()
        if not self.goal:
            self.goal = None
        elif self.goal <= Decimal('0.0'):
            raise ValidationError('Goal should be a positive value')
        if self.istarget and self.options.count() != 0:
            raise ValidationError('Targets cannot have children')
        if self.parent and self.parent.istarget:
            raise ValidationError('Cannot set that parent, parent is a target')
        if self.istarget and self.allowuseroptions:
            raise ValidationError(
                'A bid target cannot allow user options, since it cannot have children.')
        sameName = Bid.objects.filter(
            speedrun=self.speedrun, event=self.event, parent=self.parent, name__iexact=self.name)
        if sameName.exists():
            if sameName.count() > 1 or sameName[0].id != self.id:
                raise ValidationError(
                    'Cannot have a bid under the same event/run/parent with the same name')
        if self.id == None or (sameName.exists() and sameName[0].state == 'HIDDEN' and self.state == 'OPENED'):
            self.revealedtime = datetime.utcnow().replace(tzinfo=pytz.utc)
        self.update_total()

    @property
    def has_options(self):
        return self.allowuseroptions or self.public_options.exists()

    @property
    def public_options(self):
        return self.options.filter(Q(state='OPENED') | Q(state='CLOSED')).order_by('-total')

    def update_total(self):
        if self.istarget:
            self.total = self.bids.filter(donation__transactionstate='COMPLETED').aggregate(
                Sum('amount'))['amount__sum'] or Decimal('0.00')
            self.count = self.bids.filter(
                donation__transactionstate='COMPLETED').count()
            # auto close this if it's a challenge with no children and the goal's been met
            if self.goal and self.state == 'OPENED' and self.total >= self.goal and self.istarget:
                self.state = 'CLOSED'
        else:
            options = self.options.exclude(state__in=(
                'HIDDEN', 'DENIED', 'PENDING')).aggregate(Sum('total'), Sum('count'))
            self.total = options['total__sum'] or Decimal('0.00')
            self.count = options['count__sum'] or 0

    def get_event(self):
        if self.speedrun:
            return self.speedrun.event
        else:
            return self.event

    def full_label(self, addMoney=True):
        result = [self.fullname()]
        if self.speedrun:
            result = [self.speedrun.name_with_category(), ' : '] + result
        if addMoney:
            result += [' $', '%0.2f' % self.total]
            if self.goal:
                result += [' / ', '%0.2f' % self.goal]
        return ''.join(result)

    def __unicode__(self):
        if self.parent:
            return unicode(self.parent) + ' -- ' + self.name
        elif self.speedrun:
            return self.speedrun.name_with_category() + ' -- ' + self.name
        else:
            return unicode(self.event) + ' -- ' + self.name

    def fullname(self):
        return ((self.parent.fullname() + ' -- ') if self.parent else '') + self.name