Пример #1
0
class Command(NoArgsCommand):
    default_interval = 60

    help = dedent(u"""\
        Dummy cron server for local development. Repeatedly calls ``runcrons`` management command
        executing all sheduled cron jobs.""")

    option_list = NoArgsCommand.option_list + (
        make_option(u'--interval', action=u'store', type=u'int', dest=u'interval',
            default=default_interval, help=squeeze(u"""
                Interval in seconds how often to check if there are jobs to run. Defaults to {}
                secons.
                """).format(default_interval)),
        make_option(u'--clearlogs', action=u'store_true', dest=u'clearlogs', default=False,
            help=squeeze(u"""
                Clear cron logs before running the server like no cron jobs have ever been run yet.
                """)),
        )

    def handle_noargs(self, **options):
        interval = options[u'interval']
        clearlogs = options[u'clearlogs']

        try:
            if clearlogs:
                CronJobLog.objects.all().delete()
            while True:
                # If we are timewarping, we may encounter cron logs from future. We must remove
                # them, otherwise django_cron won't run any jobs with logs from furure.
                CronJobLog.objects.filter(end_time__gt=utc_now()).delete()

                call_command(u'runcrons')
                time.sleep(interval)
        except KeyboardInterrupt:
            pass
Пример #2
0
class InvitationAdminBulkAddForm(forms.Form):
    emails = forms.CharField(
            validators=[ValidatorChain(
                validate_comma_separated_emails,
                validate_unused_emails,
                )],
            widget=admin.widgets.AdminTextareaWidget(),
            help_text=squeeze(u"""
                Comma separated list of email address to invite.
                """),
            )
    validity = forms.IntegerField(
            min_value=0,
            initial=app_settings.DEFAULT_VALIDITY,
            help_text=squeeze(u"""
                Number of days the invitation will be valid.
                """),
            )
    invitor = Invitation._meta.get_field(u'invitor').formfield(
            widget=admin.widgets.ForeignKeyRawIdWidget(
                Invitation._meta.get_field(u'invitor').rel, admin.site),
            )
    send_email = forms.BooleanField(
            initial=True,
            required=False,
            help_text=squeeze(u"""
                Check to send the invitation e-mail to the given address. Leave the checkbox empty
                if you just want to create a fake invitation without sending any real e-mail. For
                instance, if you want to send the invitation e-mail manually.
                """),
            )

    class _meta:
        model = Invitation
        labels = None
        help_texts = None

    def __init__(self, *args, **kwargs):
        self.instance = Invitation()
        user = kwargs.pop(u'user')
        super(InvitationAdminBulkAddForm, self).__init__(*args, **kwargs)

        self.initial[u'invitor'] = user

    def save(self):
        assert self.is_valid()

        invitations = []
        for name, email in getaddresses([self.cleaned_data[u'emails']]):
            invitation = Invitation.create(
                    email=email,
                    invitor=self.cleaned_data[u'invitor'],
                    validity=self.cleaned_data[u'validity'],
                    send_email=self.cleaned_data[u'send_email'],
                    )
            invitation.save()
            invitations.append(invitation)

        return invitations
Пример #3
0
class District(FormatMixin, models.Model): # "Okres"
    # Primary key
    id = models.CharField(max_length=32, primary_key=True,
            help_text=squeeze(u"""
                District primary key. Example: "SK031B" (REGPJ.LSUJ1)
                """))

    # Should NOT be empty
    name = models.CharField(max_length=255, unique=True,
            help_text=squeeze(u"""
                Unique human readable district name. (REGPJ.NAZOKS, REGPJ.NAZLSUJ1)
                """))

    # Should NOT be empty; Read-only; Automaticly computed in save()
    slug = models.SlugField(max_length=255, unique=True,
            help_text=squeeze(u"""
                Unique slug to identify the district used in urls. Automaticly computed from the
                district name. May not be changed manually.
                """))

    # May NOT be NULL
    region = models.ForeignKey(Region, help_text=u'Region the district belongs to.')

    # Backward relations:
    #
    #  -- municipality_set: by Municipality.district
    #     May be empty
    #
    #  -- neighbourhood_set: by Neighbourhood.district
    #     May be empty

    # Backward relations added to other models:
    #
    #  -- Region.district_set
    #     May be empty

    # Indexes:
    #  -- id:     primary_key
    #  -- name:   unique
    #  -- slug:   unique
    #  -- region: ForeignKey

    objects = DistrictQuerySet.as_manager()

    @decorate(prevent_bulk_create=True)
    def save(self, *args, **kwargs):
        update_fields = kwargs.get(u'update_fields', None)

        # Generate and save slug if saving name
        if update_fields is None or u'name' in update_fields:
            self.slug = slugify(self.name)
            if update_fields is not None:
                update_fields.append(u'slug')

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

    def __unicode__(self):
        return u'[{}] {}'.format(self.pk, self.name)
Пример #4
0
class ObligeeAlias(FormatMixin, models.Model):
    # May NOT be NULL
    obligee = models.ForeignKey(Obligee,
                                help_text=u'Obligee of which this is alias.')

    # Should NOT be empty
    name = models.CharField(max_length=255,
                            unique=True,
                            help_text=squeeze(u"""
                Unique human readable obligee alias if the obligee has multiple common names.
                """))

    # Should NOT be empty; Read-only; Automaticly computed in save()
    slug = models.SlugField(max_length=255,
                            unique=True,
                            help_text=squeeze(u"""
                Unique slug to identify the obligee alias used in urls. Automaticly computed from
                the obligee name. May not be changed manually.
                """))

    # May be empty
    description = models.TextField(blank=True,
                                   help_text=u'Obligee alias description.')

    # May be empty
    notes = models.TextField(
        blank=True,
        help_text=u'Internal freetext notes. Not shown to the user.')

    # Backward relations added to other models:
    #
    #  -- Obligee.obligeealias_set
    #     May be empty

    # Indexes:
    #  -- obligee: ForeignKey
    #  -- name:    unique
    #  -- slug:    unique

    objects = ObligeeAliasQuerySet.as_manager()

    class Meta:
        verbose_name_plural = u'obligee aliases'

    @decorate(prevent_bulk_create=True)
    def save(self, *args, **kwargs):
        update_fields = kwargs.get(u'update_fields', None)

        # Generate and save slug if saving name
        if update_fields is None or u'name' in update_fields:
            self.slug = slugify(self.name)
            if update_fields is not None:
                update_fields.append(u'slug')

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

    def __unicode__(self):
        return u'[{}] {}'.format(self.pk, self.name)
Пример #5
0
class ObligeeGroup(FormatMixin, models.Model):
    # May NOT be empty
    key = models.CharField(max_length=255,
                           unique=True,
                           help_text=squeeze(u"""
                Unique key to identify the group. The key is a path of slash separated words each
                of which represents a parent group. Every word in the path should be a nonempty
                string and should only contain alphanumeric characters, underscores and hyphens.
                """))

    # Should NOT be empty
    name = models.CharField(max_length=255,
                            unique=True,
                            help_text=squeeze(u"""
                Unique human readable group name.
                """))

    # Should NOT be empty; Read-only; Automaticly computed in save()
    slug = models.SlugField(max_length=255,
                            unique=True,
                            help_text=squeeze(u"""
                Unique slug to identify the group used in urls. Automaticly computed from the group
                name. May not be changed manually.
                """))

    # May be empty
    description = models.TextField(blank=True,
                                   help_text=squeeze(u"""
                Human readable group description.
                """))

    # Backward relations:
    #
    #  -- obligee_set: by Obligee.groups
    #     May be empty

    # Indexes:
    #  -- key:  unique
    #  -- name: unique
    #  -- slug: unique

    objects = ObligeeGroupQuerySet.as_manager()

    @decorate(prevent_bulk_create=True)
    def save(self, *args, **kwargs):
        update_fields = kwargs.get(u'update_fields', None)

        # Generate and save slug if saving name
        if update_fields is None or u'name' in update_fields:
            self.slug = slugify(self.name)
            if update_fields is not None:
                update_fields.append(u'slug')

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

    def __unicode__(self):
        return u'[{}] {}'.format(self.pk, self.key)
Пример #6
0
class InforequestDraft(models.Model):
    # May NOT be NULL
    applicant = models.ForeignKey(User,
            help_text=squeeze(u"""
                The draft owner, the future inforequest applicant.
                """))

    # May be NULL
    obligee = models.ForeignKey(u'obligees.Obligee', blank=True, null=True,
            help_text=squeeze(u"""
                The obligee the inforequest will be sent to, if the user has already set it.
                """))

    # May be empty; Django migrations for MySQL backend are broken if ``default`` is mutable.
    subject = JSONField(blank=True, default=())
    content = JSONField(blank=True, default=())

    # May be empty
    attachment_set = generic.GenericRelation(u'attachments.Attachment', content_type_field=u'generic_type', object_id_field=u'generic_id')

    # Backward relations added to other models:
    #
    #  -- User.inforequestdraft_set
    #     May be empty
    #
    #  -- Obligee.inforequestdraft_set
    #     May be empty

    objects = InforequestDraftQuerySet.as_manager()

    class Meta:
        index_together = [
                # [u'applicant'] -- ForeignKey defines index by default
                # [u'obligee'] -- ForeignKey defines index by default
                ]

    @staticmethod
    def prefetch_attachments(path=None, queryset=None):
        u"""
        Use to prefetch ``InforequestDraft.attachments``
        """
        if queryset is None:
            queryset = Attachment.objects.get_queryset()
        queryset = queryset.order_by_pk()
        return Prefetch(join_lookup(path, u'attachment_set'), queryset, to_attr=u'attachments')

    @cached_property
    def attachments(self):
        u"""
        Cached list of all inforequest draft attachments ordered by ``pk``. May be prefetched with
        ``prefetch_related(InforequestDraft.prefetch_attachments())`` queryset method.
        """
        return list(self.attachment_set.order_by_pk())

    def __unicode__(self):
        return u'%s' % self.pk
Пример #7
0
class Region(FormatMixin, models.Model): # "Kraj"
    # Primary key
    id = models.CharField(max_length=32, primary_key=True,
            help_text=squeeze(u"""
                Region primary key. Example: "SK031" (REGPJ.RSUJ3)
                """))

    # Should NOT be empty
    name = models.CharField(max_length=255, unique=True,
            help_text=squeeze(u"""
                Unique human readable region name. (REGPJ.NAZKRJ, REGPJ.NAZRSUJ3)
                """))

    # Should NOT be empty; Read-only; Automaticly computed in save()
    slug = models.SlugField(max_length=255, unique=True,
            help_text=squeeze(u"""
                Unique slug to identify the region used in urls. Automaticly computed from the
                region name. May not be changed manually.
                """))

    # Backward relations:
    #
    #  -- district_set: by District.region
    #     May be empty
    #
    #  -- municipality_set: by Municipality.region
    #     May be empty
    #
    #  -- neighbourhood_set: by Neighbourhood.region
    #     May be empty

    # Indexes:
    #  -- id:   primary_key
    #  -- name: unique
    #  -- slug: unique

    objects = RegionQuerySet.as_manager()

    @decorate(prevent_bulk_create=True)
    def save(self, *args, **kwargs):
        update_fields = kwargs.get(u'update_fields', None)

        # Generate and save slug if saving name
        if update_fields is None or u'name' in update_fields:
            self.slug = slugify(self.name)
            if update_fields is not None:
                update_fields.append(u'slug')

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

    def __unicode__(self):
        return u'[{}] {}'.format(self.pk, self.name)
Пример #8
0
class InvitationAdminAddForm(forms.ModelForm):
    email = forms.CharField(
            validators=[ValidatorChain(
                validate_email,
                validate_unused_emails,
                )],
            widget=admin.widgets.AdminEmailInputWidget(),
            help_text=Invitation._meta.get_field(u'email').help_text,
            )
    validity = forms.IntegerField(
            min_value=0,
            initial=app_settings.DEFAULT_VALIDITY,
            help_text=squeeze(u"""
                Number of days the invitation will be valid.
                """),
            )
    invitor = Invitation._meta.get_field(u'invitor').formfield(
            widget=admin.widgets.ForeignKeyRawIdWidget(
                Invitation._meta.get_field(u'invitor').rel, admin.site),
            )
    send_email = forms.BooleanField(
            initial=True,
            required=False,
            help_text=squeeze(u"""
                Check to send the invitation e-mail to the given address. Leave the checkbox empty
                if you just want to create a fake invitation without sending any real e-mail. For
                instance, if you want to send the invitation e-mail manually.
                """),
            )

    def __init__(self, *args, **kwargs):
        user = kwargs.pop(u'user')
        super(InvitationAdminAddForm, self).__init__(*args, **kwargs)

        self.initial[u'invitor'] = user

    def save(self, commit=True):
        assert self.is_valid()

        invitation = Invitation.create(
                email=self.cleaned_data[u'email'],
                invitor=self.cleaned_data[u'invitor'],
                validity=self.cleaned_data[u'validity'],
                send_email=self.cleaned_data[u'send_email'],
                )

        if commit:
            invitation.save()
        return invitation

    def save_m2m(self):
        pass
Пример #9
0
class ObligeeTag(FormatMixin, models.Model):
    # May NOT be empty
    key = models.CharField(max_length=255,
                           unique=True,
                           help_text=squeeze(u"""
                Unique key to identify the tag. Should contain only alphanumeric characters,
                underscores and hyphens.
                """))

    # Should NOT be empty
    name = models.CharField(max_length=255,
                            unique=True,
                            help_text=squeeze(u"""
                Unique human readable tag name.
                """))

    # Should NOT be empty; Read-only; Automaticly computed in save()
    slug = models.SlugField(max_length=255,
                            unique=True,
                            help_text=squeeze(u"""
                Unique slug to identify the tag used in urls. Automaticly computed from the tag
                name. May not be changed manually.
                """))

    # Backward relations:
    #
    #  -- obligee_set: by Obligee.tags
    #     May be empty

    # Indexes:
    #  -- key:  unique
    #  -- name: unique
    #  -- slug: unique

    objects = ObligeeTagQuerySet.as_manager()

    @decorate(prevent_bulk_create=True)
    def save(self, *args, **kwargs):
        update_fields = kwargs.get(u'update_fields', None)

        # Generate and save slug if saving name
        if update_fields is None or u'name' in update_fields:
            self.slug = slugify(self.name)
            if update_fields is not None:
                update_fields.append(u'slug')

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

    def __unicode__(self):
        return u'[{}] {}'.format(self.pk, self.key)
Пример #10
0
class Neighbourhood(FormatMixin, models.Model): # "Základná sídelná jednotka"
    # Primary key
    id = models.CharField(max_length=32, primary_key=True,
            help_text=squeeze(u"""
                Neighbourhood primary key. Example: "26289" (REGPJ.ICZSJ)
                """))

    # Should NOT be empty
    name = models.CharField(max_length=255,
            help_text=squeeze(u"""
                Human readable neighbourhood name. Neighbourhood names are not unique, not even
                within a municipality. (REGPJ.NAZZSJ)
                """))

    # Should NOT be empty
    cadastre = models.CharField(max_length=255,
            help_text=squeeze(u"""
                Human readable neighbourhood cadastre name. (REGPJ.NAZUTJ)
                """))

    # May NOT be NULL
    municipality = models.ForeignKey(Municipality,
            help_text=u'Municipality the neighbourhood belongs to.')
    district = models.ForeignKey(District,
            help_text=u'District the neighbourhood belongs to.')
    region = models.ForeignKey(Region,
            help_text=u'Region the neighbourhood belongs to.')

    # Backward relations added to other models:
    #
    #  -- Municipality.neighbourhood_set
    #     May be empty
    #
    #  -- District.neighbourhood_set
    #     May be empty
    #
    #  -- Region.neighbourhood_set
    #     May be empty

    # Indexes:
    #  -- id:                 primary_key
    #  -- region:             ForeignKey
    #  -- district:           ForeignKey
    #  -- municipality:       ForeignKey

    objects = NeighbourhoodQuerySet.as_manager()

    def __unicode__(self):
        return u'[{}] {}'.format(self.pk, self.name)
Пример #11
0
    def handle(self, *prefixes, **options):
        groups = [
                (u'CRITICALS', datacheck.CRITICAL, float(u'inf'),      color_style().ERROR),
                (u'ERRORS',    datacheck.ERROR,    datacheck.CRITICAL, color_style().ERROR),
                (u'WARNINGS',  datacheck.WARNING,  datacheck.ERROR,    color_style().WARNING),
                (u'INFOS',     datacheck.INFO,     datacheck.WARNING,  color_style().NOTICE),
                (u'DEBUGS',    0,                  datacheck.INFO,     color_style().NOTICE),
                ]

        output = []
        if options[u'list']:
            output.append(u'Available data checks:')
            output.extend(u' -- {}'.format(c) for c in datacheck.registry)
            self.stdout.write(u'\n'.join(output))
            return

        if prefixes:
            registry = datacheck.registry.filtered(prefixes)
            output.append(u'Running {} data checks:'.format(len(registry)))
            output.extend(u' -- {}'.format(c) for c in registry)
            output.append(u'')
        else:
            registry = datacheck.registry
            output.append(u'Running all registered data checks.')
            output.append(u'')

        issues = registry.run_checks(
                superficial=options[u'superficial'], autofix=options[u'autofix'])
        autofixable = len([s for s in issues if s.autofixable])
        if autofixable:
            if options[u'autofix']:
                output.append(squeeze(u"""
                    Data checks identified {} issues, {} of them were autofixed.
                    """).format(len(issues), autofixable))
            else:
                output.append(squeeze(u"""
                    Data checks identified {} issues, {} of them can be autofixed (use --autofix).
                    """).format(len(issues), autofixable))
        else:
            output.append(u'Data checks identified {} issues.'.format(len(issues)))

        for group, level_min, level_max, style in groups:
            filtered = [a for a in issues if level_min <= a.level < level_max]
            if filtered:
                output.append(u'')
                output.append(u'{}:'.format(group))
                output.extend(style(format(a)) for a in filtered)

        self.stdout.write(u'\n'.join(output))
Пример #12
0
 def do_reset(self):
     count = Inforequest.objects.count()
     if count:
         inputed = self.importer.input_yes_no(squeeze(u"""
                 Discarding current obligees will discard all existing inforequests as well.
                 There are {} inforequests.
                 """),
                 u'Are you sure, you want to discard them?', count, default=u'N')
         if inputed != u'Y':
             raise CommandError(squeeze(u"""
                     Existing inforequests prevented us from discarding current obligees.
                     """))
     self.reset_model(Obligee)
     self.reset_model(HistoricalObligee)
     self.reset_model(Inforequest)
Пример #13
0
 def do_reset(self):
     count = Inforequest.objects.count()
     if count:
         inputed = self.importer.input_yes_no(squeeze(u"""
                 Discarding current obligees will discard all existing inforequests as well.
                 There are {} inforequests.
                 """),
                 u'Are you sure, you want to discard them?', count, default=u'N')
         if inputed != u'Y':
             raise CommandError(squeeze(u"""
                     Existing inforequests prevented us from discarding current obligees.
                     """))
     self.reset_model(Obligee)
     self.reset_model(HistoricalObligee)
     self.reset_model(Inforequest)
Пример #14
0
    def send_by_email(self):
        if not self.is_applicant_action:
            raise TypeError(u'{} is not applicant action'.format(self.get_type_display()))
        if not self.branch.collect_obligee_emails:
            # Django silently ignores messages with no recipients
            raise ValueError(u'Action has no recipients')

        sender_name = self.branch.inforequest.applicant_name
        sender_address = self.branch.inforequest.unique_email
        sender_formatted = formataddr((squeeze(sender_name), sender_address))
        recipients = [formataddr(r) for r in self.branch.collect_obligee_emails]

        # FIXME: Attachment name and content type are set by client and not to be trusted. The name
        # must be sanitized and the content type white listed for known content types. Any unknown
        # content type should be replaced with 'application/octet-stream'.

        msg = EmailMessage(self.subject, self.content, sender_formatted, recipients)
        for attachment in self.attachments:
            msg.attach(attachment.name, attachment.content, attachment.content_type)
        msg.send()

        inforequestemail = InforequestEmail(
                inforequest=self.branch.inforequest,
                email=msg.instance,
                type=InforequestEmail.TYPES.APPLICANT_ACTION,
                )
        inforequestemail.save()

        self.email = msg.instance
        self.save(update_fields=[u'email'])
Пример #15
0
    def send_by_email(self):
        if not self.is_applicant_action:
            raise TypeError(u'{} is not applicant action'.format(self.get_type_display()))
        if not self.branch.collect_obligee_emails:
            # Django silently ignores messages with no recipients
            raise ValueError(u'Action has no recipients')

        sender_name = self.branch.inforequest.applicant_name
        sender_address = self.branch.inforequest.unique_email
        sender_formatted = formataddr((squeeze(sender_name), sender_address))
        recipients = [formataddr(r) for r in self.branch.collect_obligee_emails]

        # FIXME: Attachment name is set by client and may not to be trusted. It must be sanitized.

        msg = EmailMessage(self.subject, self.content, sender_formatted, recipients)
        for attachment in self.attachments:
            msg.attach(attachment.name, attachment.content, attachment.content_type)
        msg.send()

        inforequestemail = InforequestEmail(
                inforequest=self.branch.inforequest,
                email=msg.instance,
                type=InforequestEmail.TYPES.APPLICANT_ACTION,
                )
        inforequestemail.save()

        self.email = msg.instance
        self.save(update_fields=[u'email'])
Пример #16
0
    def send_by_email(self):
        if not self.is_applicant_action:
            raise TypeError(u'%s is not applicant action' %
                            self.get_type_display())

        sender_name = self.branch.inforequest.applicant_name
        sender_address = self.branch.inforequest.unique_email
        sender_formatted = formataddr((squeeze(sender_name), sender_address))
        recipients = (formataddr(r)
                      for r in self.branch.collect_obligee_emails())

        # FIXME: Attachment name and content type are set by client and not to be trusted. The name
        # must be sanitized and the content type white listed for known content types. Any unknown
        # content type should be replaced with 'application/octet-stream'.

        msg = EmailMessage(self.subject, self.content, sender_formatted,
                           recipients)
        for attachment in self.attachments:
            msg.attach(attachment.name, attachment.content,
                       attachment.content_type)
        msg.send()

        inforequestemail = InforequestEmail(
            inforequest=self.branch.inforequest,
            email=msg.instance,
            type=InforequestEmail.TYPES.APPLICANT_ACTION,
        )
        inforequestemail.save()

        self.email = msg.instance
        self.save(update_fields=[u'email'])
Пример #17
0
 def test_long_random_text(self):
     sample = u''.join(
         random.choice(string.printable
                       if random.random() < 0.5 else string.whitespace)
         for i in range(1000))
     res = squeeze(sample)
     self.assertRegexpMatches(res, r'^(\S+ )*\S+$')
Пример #18
0
def datachecks(superficial, autofix):
    u"""
    Checks that every ``Attachment`` instance has its file working, and there are not any orphaned
    attachment files.
    """
    # This check is a bit slow. We skip it if running from cron or the user asked for
    # superficial tests only.
    if superficial:
        return

    attachments = Attachment.objects.all()
    attachment_names = {a.file.name for a in attachments}

    for attachment in attachments:
        try:
            try:
                attachment.file.open(u'rb')
            finally:
                attachment.file.close()
        except IOError:
            yield datacheck.Error(u'{} is missing its file: "{}".',
                    attachment, attachment.file.name)

    field = Attachment._meta.get_field(u'file')
    if not field.storage.exists(field.upload_to):
        return
    for file_name in field.storage.listdir(field.upload_to)[1]:
        attachment_name = u'{}/{}'.format(field.upload_to, file_name)
        modified_time = utc_datetime_from_local(field.storage.modified_time(attachment_name))
        timedelta = utc_now() - modified_time
        if timedelta > datetime.timedelta(days=5) and attachment_name not in attachment_names:
            yield datacheck.Info(squeeze(u"""
                    There is no Attachment instance for file: "{}". The file is {} days old, so you
                    can probably remove it.
                    """), attachment_name, timedelta.days)
Пример #19
0
 def values(self):
     res = super(ClarificationResponseStep, self).values()
     res[u'subject'] = squeeze(
         render_to_string(
             u'inforequests/clarification_response/forms/subject.txt'))
     res[u'content'] = self.fields[u'content'].finalize(
         self.cleaned_data[u'content'])
     return res
Пример #20
0
 def test_long_text(self):
     res = squeeze(u"""
             Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
             incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
             exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
             irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
             pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
             deserunt mollit anim id est laborum.
             """)
     self.assertRegexpMatches(res, r'^(\S+ )*\S+$')
Пример #21
0
 def test_long_text(self):
     res = squeeze(u"""
             Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
             incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
             exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
             irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
             pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
             deserunt mollit anim id est laborum.
             """)
     self.assertRegexpMatches(res, r'^(\S+ )*\S+$')
Пример #22
0
    def clean(self):
        cleaned_data = super(NewActionCommonForm, self).clean()

        if not self.draft:
            if self.inforequest.has_undecided_emails:
                msg = squeeze(render_to_string(u'inforequests/messages/new_action-undecided_emails.txt', {
                        u'inforequest': self.inforequest,
                        }))
                raise forms.ValidationError(msg, code=u'undecided_emails')

        return cleaned_data
Пример #23
0
    def pre_transition(self):
        res = super(PaperStep, self).pre_transition()

        if self.accessible:
            context = self.context(dict(finalize=True))
            subject = squeeze(render_to_string(self.subject_template, context))
            content = render_to_string(self.content_template, context)
            res.globals[self.subject_value_name] = subject
            res.globals[self.content_value_name] = content

        return res
    def post_transition(self):
        res = super(Main, self).post_transition()

        if self.is_valid():
            res.globals.update({
                u'subject': squeeze(render_to_string(
                    u'inforequests/clarification_response/forms/subject.txt')),
                u'content': self.fields[u'content'].finalize(self.cleaned_data[u'content']),
                })

        return res
Пример #25
0
    def pre_transition(self):
        res = super(PaperStep, self).pre_transition()

        if self.accessible:
            context = self.context(dict(finalize=True))
            subject = squeeze(render_to_string(self.subject_template, context))
            content = render_to_string(self.content_template, context)
            res.globals[self.subject_value_name] = subject
            res.globals[self.content_value_name] = content

        return res
Пример #26
0
    def clean(self):
        cleaned_data = super(AppealFinalStep, self).clean()

        if self.wizard.branch.inforequest.has_undecided_emails:
            msg = squeeze(
                render_to_string(
                    u'inforequests/appeals/messages/undecided_emails.txt', {
                        u'inforequest': self.wizard.branch.inforequest,
                    }))
            raise forms.ValidationError(msg, code=u'undecided_emails')

        return cleaned_data
Пример #27
0
 def test_only_whitespace(self):
     self.assertEqual(squeeze(u' '), u'')
     self.assertEqual(squeeze(u'               '), u'')
     self.assertEqual(squeeze(u'  \n\n  \n\t\t\r\r\r\n\n'), u'')
     self.assertEqual(squeeze(u'\n\n\n\n\n\n\n\n\n\n'), u'')
     self.assertEqual(squeeze(u'\t\t\t\t'), u'')
     self.assertEqual(squeeze(u'\r'), u'')
Пример #28
0
 def test_only_whitespace(self):
     self.assertEqual(squeeze(u' '), u'')
     self.assertEqual(squeeze(u'               '), u'')
     self.assertEqual(squeeze(u'  \n\n  \n\t\t\r\r\r\n\n'), u'')
     self.assertEqual(squeeze(u'\n\n\n\n\n\n\n\n\n\n'), u'')
     self.assertEqual(squeeze(u'\t\t\t\t'), u'')
     self.assertEqual(squeeze(u'\r'), u'')
Пример #29
0
class InforequestEmail(FormatMixin, models.Model):
    # May NOT be NULL; m2m ends; Indexes are prefixes of [inforequest, email] and
    # [email, inforequest] indexes, respectively
    inforequest = models.ForeignKey(u'Inforequest', db_index=False)
    email = models.ForeignKey(u'mail.Message', db_index=False)

    # May NOT be NULL
    TYPES = FieldChoices(
            # For outbound messages
            (u'APPLICANT_ACTION', 1, _(u'inforequests:InforequestEmail:type:APPLICANT_ACTION')),
            # For inbound messages
            (u'OBLIGEE_ACTION',   2, _(u'inforequests:InforequestEmail:type:OBLIGEE_ACTION')),
            (u'UNDECIDED',        3, _(u'inforequests:InforequestEmail:type:UNDECIDED')),
            (u'UNRELATED',        4, _(u'inforequests:InforequestEmail:type:UNRELATED')),
            (u'UNKNOWN',          5, _(u'inforequests:InforequestEmail:type:UNKNOWN')),
            )
    type = models.SmallIntegerField(choices=TYPES._choices,
            help_text=squeeze(u"""
                "Applicant Action": the email represents an applicant action;
                "Obligee Action": the email represents an obligee action;
                "Undecided": The email is waiting for applicant decision;
                "Unrelated": Marked as an unrelated email;
                "Unknown": Marked as an email the applicant didn't know how to decide.
                It must be "Applicant Action" for outbound mesages or one of the remaining values
                for inbound messages.
                """
                ))

    # Backward relations added to other models:
    #
    #  -- Inforequest.inforequestemail_set
    #     May be empty
    #
    #  -- Message.inforequestemail_set
    #     May be empty

    # Indexes:
    #  -- email, inforequest: index_together
    #  -- inforequest, email: index_together
    #  -- type, inforequest:  index_together

    objects = InforequestEmailQuerySet.as_manager()

    class Meta:
        index_together = [
                [u'email', u'inforequest'],
                [u'inforequest', u'email'],
                [u'type', u'inforequest'],
                ]

    def __unicode__(self):
        return format(self.pk)
Пример #30
0
class LoadSheetsCommand(BaseCommand):
    help = u'Loads .xlsx files with data'
    args = u'file [file ...]'
    option_list = BaseCommand.option_list + (
        make_option(u'--dry-run',
                    action=u'store_true',
                    default=False,
                    help=squeeze(u"""
                Just show if the files would be imported correctly. Rollback all changes at the
                end.
                """)),
        make_option(u'--reset',
                    action=u'store_true',
                    default=False,
                    help=squeeze(u"""
                Discard current data before imporing the files. Only data from sheets present in
                the files are discarded. Data from missing sheets are left untouched.
                """)),
        make_option(u'--assume',
                    choices=[u'yes', u'no', u'default'],
                    help=squeeze(u"""
                Assume yes/no/default answer to all yes/no questions.
                """)),
    )
    importer = Importer
    book = None

    def handle(self, *args, **options):
        if not args:
            raise CommandError(u'No file specified.')

        try:
            importer = self.importer(self.book, options, self.stdout)
            importer.do_import(args)
        except (KeyboardInterrupt, EOFError):
            self.stdout.write(u'\n')
            raise CommandError(u'Aborted')
        except RollbackDryRun:
            pass
Пример #31
0
    def values(self):
        res = super(WizardPaperStep, self).values()

        subject = squeeze(
            render_to_string(self.subject_template,
                             self.context(dict(finalize=True))))
        content = render_to_string(self.content_template,
                                   self.context(dict(finalize=True)))
        res.update({
            self.subject_value_name: subject,
            self.content_value_name: content,
        })

        return res
    def post_transition(self):
        res = super(Main, self).post_transition()

        if self.is_valid():
            res.globals.update({
                u'subject':
                squeeze(
                    render_to_string(
                        u'inforequests/clarification_response/forms/subject.txt'
                    )),
                u'content':
                self.fields[u'content'].finalize(
                    self.cleaned_data[u'content']),
            })

        return res
Пример #33
0
def render_mail(template_prefix, dictionary=None, **kwargs):
    u"""
    Create ``django.core.mail.EmailMessage`` object ready to be sent with ``msg.send()`` method.
    Message subject and body are rendered using templates "(prefix)_subject.txt" and
    "(prefix)_message.txt" and/or "(prefix)_message.html". If both ".txt" and ".html" body
    templates exist, the created message is multipart/alternativea including its text and html
    versions.

    The functions accepts additional keyword arguments for EmailMessage constructor. Of most
    interest are: ``from_email``, ``to``, ``bcc``, ``attachments``, ``headers`` and ``cc``.

    Based on: Django-allauth's allauth.DefaultAccountAdapter.render_mail method.

    Examples:
        render_mail('app/mail',
                    from_email='My Name <*****@*****.**>',
                    to=['Your Name <*****@*****.**>'])
    """
    site = Site.objects.get_current()
    subject = render_to_string(u'{}_subject.txt'.format(template_prefix),
                               dictionary)
    subject = squeeze(u'[{}] {}'.format(site.name, subject))

    bodies = {}
    for ext in [u'html', u'txt']:
        template_name = u'{}_message.{}'.format(template_prefix, ext)
        try:
            bodies[ext] = render_to_string(template_name, dictionary).strip()
        except TemplateDoesNotExist:
            # We need at least one body
            if ext == u'txt' and not bodies:
                raise

    if u'txt' in bodies and u'html' in bodies:
        msg = EmailMultiAlternatives(subject, bodies[u'txt'], **kwargs)
        msg.attach_alternative(bodies[u'html'], u'text/html')
    elif u'html' in bodies:
        msg = EmailMessage(subject, bodies[u'html'], **kwargs)
        msg.content_subtype = u'html'  # Main content is now text/html
    else:
        msg = EmailMessage(subject, bodies[u'txt'], **kwargs)

    return msg
Пример #34
0
    def __init__(self, page, file, create, *args, **kwargs):
        super(FileEditForm, self).__init__(*args, **kwargs)
        self.page = page
        self.file = file
        self.create = create

        self.fieldsets = [
            Bunch(label=None, collapse=False, fields=[]),
        ]

        self.fieldsets[0].fields.append(
            FakeField(u'Page', LivePath.render(page.lang, page.path)))

        self.fields[u'name'] = forms.CharField(
            label=u'File Name',
            validators=[
                RegexValidator(
                    pages.file_regex,
                    squeeze(u"""
                    Enter a valid file name. Only letters, numbers, dashes and dots are allowed.
                    """)),
            ],
        )
        if not create:
            self.initial[u'name'] = file.name
        self.fieldsets[0].fields.append(self[u'name'])

        if not create:
            self.fieldsets[0].fields.append(
                FakeField(u'Content Type', file.content_type))
            self.fieldsets[0].fields.append(
                FakeField(
                    u'Modified',
                    date_format(datetime.datetime.fromtimestamp(file.mtime),
                                u'DATETIME_FORMAT')))

        self.fields[u'content'] = forms.FileField(
            label=u'Content',
            widget=PreviewFileInput,
        )
        if not create:
            self.initial[u'content'] = file
        self.fieldsets[0].fields.append(self[u'content'])
Пример #35
0
def render_mail(template_prefix, dictionary=None, **kwargs):
    u"""
    Create ``django.core.mail.EmailMessage`` object ready to be sent with ``msg.send()`` method.
    Message subject and body are rendered using templates "(prefix)_subject.txt" and
    "(prefix)_message.txt" and/or "(prefix)_message.html". If both ".txt" and ".html" body
    templates exist, the created message is multipart/alternativea including its text and html
    versions.

    The functions accepts additional keyword arguments for EmailMessage constructor. Of most
    interest are: ``from_email``, ``to``, ``bcc``, ``attachments``, ``headers`` and ``cc``.

    Based on: Django-allauth's allauth.DefaultAccountAdapter.render_mail method.

    Examples:
        render_mail('app/mail',
                    from_email='My Name <*****@*****.**>',
                    to=['Your Name <*****@*****.**>'])
    """
    site = Site.objects.get_current()
    subject = render_to_string(u'{}_subject.txt'.format(template_prefix), dictionary)
    subject = squeeze(u'[{}] {}'.format(site.name, subject))

    bodies = {}
    for ext in [u'html', u'txt']:
        template_name = u'{}_message.{}'.format(template_prefix, ext)
        try:
            bodies[ext] = render_to_string(template_name, dictionary).strip()
        except TemplateDoesNotExist:
            # We need at least one body
            if ext == u'txt' and not bodies:
                raise

    if u'txt' in bodies and u'html' in bodies:
        msg = EmailMultiAlternatives(subject, bodies[u'txt'], **kwargs)
        msg.attach_alternative(bodies[u'html'], u'text/html')
    elif u'html' in bodies:
        msg = EmailMessage(subject, bodies[u'html'], **kwargs)
        msg.content_subtype = u'html' # Main content is now text/html
    else:
        msg = EmailMessage(subject, bodies[u'txt'], **kwargs)

    return msg
Пример #36
0
    def __init__(self, page, file, create, *args, **kwargs):
        super(FileEditForm, self).__init__(*args, **kwargs)
        self.page = page
        self.file = file
        self.create = create

        self.fieldsets = [
                Bunch(label=None, collapse=False, fields=[]),
                ]

        self.fieldsets[0].fields.append(FakeField(u'Page', LivePath.render(page.lang, page.path)))

        self.fields[u'name'] = forms.CharField(
            label=u'File Name',
            validators=[
                RegexValidator(pages.file_regex, squeeze(u"""
                    Enter a valid file name. Only letters, numbers, dashes and dots are allowed.
                    """)),
                ],
            )
        if not create:
            self.initial[u'name'] = file.name
        self.fieldsets[0].fields.append(self[u'name'])

        if not create:
            self.fieldsets[0].fields.append(FakeField(u'Content Type', file.content_type))
            self.fieldsets[0].fields.append(FakeField(u'Modified',
                date_format(datetime.datetime.fromtimestamp(file.mtime), u'DATETIME_FORMAT')))

        self.fields[u'content'] = forms.FileField(
            label=u'Content',
            widget=PreviewFileInput,
            )
        if not create:
            self.initial[u'content'] = file
        self.fieldsets[0].fields.append(self[u'content'])
Пример #37
0
class Branch(FormatMixin, models.Model):
    # May NOT be NULL; Index is prefix of [inforequest, advanced_by] index
    inforequest = models.ForeignKey(u'Inforequest', db_index=False)

    # May NOT be NULL
    obligee = models.ForeignKey(
        u'obligees.Obligee',
        help_text=u'The obligee the inforequest was sent or advanced to.')

    # May NOT be NULL; Automaticly frozen in save() when creating a new object
    historicalobligee = models.ForeignKey(u'obligees.HistoricalObligee',
                                          help_text=squeeze(u"""
                Frozen Obligee at the time the Inforequest was submitted or advanced to it.
                """))

    # Advancement action that advanced the inforequest to this obligee; None if it's inforequest
    # main branch. Inforequest must contain exactly one branch with ``advanced_by`` set to None;
    # Index is prefix of [advanced_by, inforequest] index
    advanced_by = models.ForeignKey(u'Action',
                                    related_name=u'advanced_to_set',
                                    blank=True,
                                    null=True,
                                    db_index=False,
                                    help_text=squeeze(u"""
                NULL for main branches. The advancement action the inforequest was advanced by for
                advanced branches. Every Inforequest must contain exactly one main branch.
                """))

    # Backward relations:
    #
    #  -- action_set: by Action.branch
    #     May NOT be empty; The first action of every main branch must be REQUEST and the first
    #     action of every advanced branch ADVANCED_REQUEST.

    # Backward relations added to other models:
    #
    #  -- Inforequest.branch_set
    #     May NOT be empty
    #
    #  -- Obligee.branch_set
    #     May be empty
    #
    #  -- HistoricalObligee.branch_set
    #     May be empty
    #
    #  -- Action.advanced_to_set
    #     May be empty

    # Indexes:
    #  -- inforequest, advanced_by: index_together
    #  -- advanced_by, inforequest: index_together
    #  -- obligee: ForeignKey
    #  -- historicalobligee: ForeignKey

    objects = BranchQuerySet.as_manager()

    class Meta:
        verbose_name_plural = u'Branches'
        index_together = [
            [u'inforequest', u'advanced_by'],
            [u'advanced_by', u'inforequest'],
        ]

    @cached_property
    def is_main(self):
        return self.advanced_by_id is None

    @staticmethod
    def prefetch_actions(path=None, queryset=None):
        u"""
        Use to prefetch ``Branch.actions``.
        """
        if queryset is None:
            queryset = Action.objects.get_queryset()
        queryset = queryset.order_by_created()
        return Prefetch(join_lookup(path, u'action_set'),
                        queryset,
                        to_attr=u'actions')

    @cached_property
    def actions(self):
        u"""
        Cached list of all branch actions ordered by ``created``. The list should not be empty. May
        be prefetched with ``prefetch_related(Branch.prefetch_actions())`` queryset method.
        """
        return list(self.action_set.order_by_created())

    @staticmethod
    def prefetch_actions_by_email(path=None, queryset=None):
        u"""
        Use to prefetch ``Branch.actions_by_email``.
        """
        if queryset is None:
            queryset = Action.objects.get_queryset()
        queryset = queryset.by_email()
        queryset = queryset.order_by_created()
        return Prefetch(join_lookup(path, u'action_set'),
                        queryset,
                        to_attr=u'actions_by_email')

    @cached_property
    def actions_by_email(self):
        u"""
        Cached list of all branch actions sent by email ordered by ``created``. May be prefetched
        with ``prefetch_related(Branch.prefetch_actions_by_email())`` queryset method. Takes
        advantage of ``Branch.actions`` if it is fetched already.
        """
        if u'actions' in self.__dict__:
            return list(a for a in self.actions if a.is_by_email)
        else:
            return list(self.action_set.by_email().order_by_created())

    @staticmethod
    def prefetch_last_action(path=None, queryset=None):
        u"""
        Use to prefetch ``Branch.last_action``. Redundant if ``prefetch_actions()`` is already
        used.
        """
        if queryset is None:
            queryset = Action.objects.get_queryset()
        quote_name = connection.ops.quote_name
        queryset = queryset.extra(where=[
            u'{action}.{pk} = ('
            u'SELECT p.{pk} '
            u'FROM {action} p '
            u'WHERE p.{branch} = {action}.{branch} '
            u'ORDER BY p.{created} DESC, p.{pk} DESC '
            u'LIMIT 1'
            u')'.format(
                action=quote_name(Action._meta.db_table),
                pk=quote_name(Action._meta.pk.column),
                branch=quote_name(Action._meta.get_field(u'branch').column),
                created=quote_name(Action._meta.get_field(u'created').column),
            )
        ])
        return Prefetch(join_lookup(path, u'action_set'),
                        queryset,
                        to_attr=u'_last_action')

    @cached_property
    def last_action(self):
        u"""
        Cached last branch action. Returns None if the branch has no actions. May be prefetched
        with ``prefetch_related(Branch.prefetch_last_action())`` queryset method. Takes advantage
        of ``Branch.actions`` if it is fetched already.
        """
        if u'_last_action' in self.__dict__:
            try:
                return self._last_action[0]
            except IndexError:
                return None
        elif u'actions' in self.__dict__:
            try:
                return self.actions[-1]
            except IndexError:
                return None
        else:
            return self.action_set.order_by_created().last()

    @cached_property
    def can_add_request(self):
        return False

    @cached_property
    def can_add_clarification_response(self):
        return (self.last_action.type == Action.TYPES.CLARIFICATION_REQUEST
                and self.last_action.deadline.calendar_days_behind <= 3)

    @cached_property
    def can_add_appeal(self):
        if self.last_action.type in [
                Action.TYPES.REQUEST,
                Action.TYPES.CLARIFICATION_RESPONSE,
                Action.TYPES.CONFIRMATION,
                Action.TYPES.EXTENSION,
                Action.TYPES.REMANDMENT,
                Action.TYPES.ADVANCED_REQUEST,
        ]:
            # All these actions have deadlines
            return self.last_action.deadline.is_deadline_missed

        if self.last_action.type == Action.TYPES.ADVANCEMENT:
            # Advancement has no deadline defined
            return (local_today() - self.last_action.delivered_date).days <= 7

        if self.last_action.type == Action.TYPES.REFUSAL:
            return self.last_action.deadline.calendar_days_behind <= 7

        if self.last_action.type == Action.TYPES.DISCLOSURE:
            return (self.last_action.disclosure_level !=
                    Action.DISCLOSURE_LEVELS.FULL
                    and self.last_action.deadline.calendar_days_behind <= 47)

        if self.last_action.type == Action.TYPES.EXPIRATION:
            return self.last_action.deadline.calendar_days_behind <= 47

        return False

    @cached_property
    def can_add_confirmation(self):
        return self.last_action.type in [
            Action.TYPES.REQUEST,
            Action.TYPES.ADVANCED_REQUEST,
        ]

    @cached_property
    def can_add_extension(self):
        return self.last_action.type in [
            Action.TYPES.REQUEST,
            Action.TYPES.CONFIRMATION,
            Action.TYPES.CLARIFICATION_RESPONSE,
            Action.TYPES.REMANDMENT,
            Action.TYPES.ADVANCED_REQUEST,
        ]

    @cached_property
    def can_add_advancement(self):
        return self.last_action.type in [
            Action.TYPES.REQUEST,
            Action.TYPES.CLARIFICATION_RESPONSE,
            Action.TYPES.CONFIRMATION,
            Action.TYPES.ADVANCED_REQUEST,
        ]

    @cached_property
    def can_add_clarification_request(self):
        return self.last_action.type in [
            Action.TYPES.REQUEST,
            Action.TYPES.CLARIFICATION_RESPONSE,
            Action.TYPES.CONFIRMATION,
            Action.TYPES.CLARIFICATION_REQUEST,
            Action.TYPES.ADVANCED_REQUEST,
        ]

    @cached_property
    def can_add_disclosure(self):
        return self.last_action.type in [
            Action.TYPES.REQUEST,
            Action.TYPES.CLARIFICATION_RESPONSE,
            Action.TYPES.CONFIRMATION,
            Action.TYPES.EXTENSION,
            Action.TYPES.REMANDMENT,
            Action.TYPES.ADVANCED_REQUEST,
        ]

    @cached_property
    def can_add_refusal(self):
        return self.last_action.type in [
            Action.TYPES.REQUEST,
            Action.TYPES.CLARIFICATION_RESPONSE,
            Action.TYPES.CONFIRMATION,
            Action.TYPES.EXTENSION,
            Action.TYPES.REMANDMENT,
            Action.TYPES.ADVANCED_REQUEST,
        ]

    @cached_property
    def can_add_affirmation(self):
        return self.last_action.type == Action.TYPES.APPEAL

    @cached_property
    def can_add_reversion(self):
        return self.last_action.type == Action.TYPES.APPEAL

    @cached_property
    def can_add_remandment(self):
        return self.last_action.type == Action.TYPES.APPEAL

    @cached_property
    def can_add_applicant_action(self):
        return self.can_add_action(*Action.APPLICANT_ACTION_TYPES)

    @cached_property
    def can_add_applicant_email_action(self):
        return self.can_add_action(*Action.APPLICANT_EMAIL_ACTION_TYPES)

    @cached_property
    def can_add_obligee_action(self):
        return self.can_add_action(*Action.OBLIGEE_ACTION_TYPES)

    @cached_property
    def can_add_obligee_email_action(self):
        return self.can_add_action(*Action.OBLIGEE_EMAIL_ACTION_TYPES)

    def can_add_action(self, *action_types):
        for action_type in action_types:
            type_name = Action.TYPES._inverse[action_type]
            if getattr(self, u'can_add_{}'.format(type_name.lower())):
                return True
        return False

    @classmethod
    def create(cls, *args, **kwargs):
        action_args = kwargs.pop(u'action_args', None) or []
        action_kwargs = kwargs.pop(u'action_kwargs', None) or {}
        branch = Branch(*args, **kwargs)

        assert (action_kwargs[u'type'] in [
            Action.TYPES.REQUEST, Action.TYPES.ADVANCED_REQUEST
        ], u'Branch must be created with a request or an advanced request action'
                )

        @after_saved(branch)
        def deferred(branch):
            action = Action.create(branch=branch,
                                   *action_args,
                                   **action_kwargs)
            action.save()

        return branch

    @decorate(prevent_bulk_create=True)
    def save(self, *args, **kwargs):
        if self.pk is None:  # Creating a new object
            assert self.obligee_id is not None, u'Branch.obligee is mandatory'
            assert self.historicalobligee_id is None, u'Branch.historicalobligee is read-only'
            self.historicalobligee = self.obligee.history.first()

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

    def add_expiration_if_expired(self):
        if not self.last_action.has_obligee_deadline_missed:
            return

        if self.last_action.type == Action.TYPES.APPEAL:
            action_type = Action.TYPES.APPEAL_EXPIRATION
        else:
            action_type = Action.TYPES.EXPIRATION

        expiration = Action.create(
            branch=self,
            type=action_type,
            legal_date=self.last_action.deadline.deadline_date,
        )
        expiration.save()

    @cached_property
    def collect_obligee_emails(self):
        res = {}
        for action in self.actions_by_email:
            if action.email.type == action.email.TYPES.INBOUND:
                res.update({action.email.from_mail: action.email.from_name})
            else:  # OUTBOUND
                res.update({r.mail: r.name for r in action.email.recipients})
        # Current obligee emails
        res.update({mail: name for name, mail in self.obligee.emails_parsed})

        return [(name, mail) for mail, name in res.items()]

    def __unicode__(self):
        return format(self.pk)
Пример #38
0
    def send_message(self, message):
        assert message.type == message.TYPES.OUTBOUND
        assert message.processed is None

        # Based on djrill.mail.backends.DjrillBackend; We can't use Djrill directly because it
        # sends the mail synchronously during user requests.
        msg = {}
        msg[u'subject'] = message.subject
        msg[u'from_email'] = message.from_mail
        if message.from_name:
            msg[u'from_name'] = message.from_name
        if message.html:
            msg[u'html'] = message.html
        if message.text:
            msg[u'text'] = message.text
        if message.headers:
            msg[u'headers'] = message.headers

        msg[u'to'] = []
        recipients = defaultdict(list)
        for recipient in message.recipients:
            rcp = {}
            rcp[u'email'] = recipient.mail
            if recipient.name:
                rcp[u'name'] = recipient.name
            if recipient.type == recipient.TYPES.TO:
                rcp[u'type'] = u'to'
            elif recipient.type == recipient.TYPES.CC:
                rcp[u'type'] = u'cc'
            else:
                assert recipient.type == recipient.TYPES.BCC
                rcp[u'type'] = u'bcc'
            msg[u'to'].append(rcp)
            recipients[recipient.mail].append(recipient)

        msg[u'attachments'] = []
        for attachment in message.attachments:
            attch = {}
            attch[u'type'] = attachment.content_type
            attch[u'name'] = attachment.name
            attch[u'content'] = base64.b64encode(attachment.content)
            msg[u'attachments'].append(attch)

        data = {}
        data[u'key'] = self.api_key
        data[u'message'] = msg

        response = requests.post(self.api_send, data=json.dumps(data))

        if response.status_code != 200:
            raise RuntimeError(squeeze(u"""
                    Sending Message(pk={}) failed with status code {}. Mandrill response: {}
                    """).format(message.pk, response.status_code, response.text))

        for rcp in response.json():
            for recipient in recipients[rcp[u'email']]:
                recipient.remote_id = rcp[u'_id']

                if rcp[u'status'] == u'sent':
                    recipient.status = recipient.STATUSES.SENT
                elif rcp[u'status'] in [u'queued', u'scheduled']:
                    recipient.status = recipient.STATUSES.QUEUED
                elif rcp[u'status'] == u'rejected':
                    recipient.status = recipient.STATUSES.REJECTED
                    recipient.status_details = rcp[u'reject_reason']
                elif rcp[u'status'] == u'invalid':
                    recipient.status = recipient.STATUSES.INVALID
                else:
                    recipient.status = recipient.STATUSES.UNDEFINED

                recipient.save(update_fields=[u'remote_id', u'status', u'status_details'])
Пример #39
0
 def test_with_only_untranslated_template(self):
     # Existing: first.html
     # Missing: first.de.html
     with translation(u'de'):
         rendered = squeeze(render_to_string(u'first.html'))
         self.assertEqual(rendered, u'(first.html)')
Пример #40
0
 def test_translated_template_has_priority(self):
     # Existing: first.html, first.en.html
     with translation(u'en'):
         rendered = squeeze(render_to_string(u'first.html'))
         self.assertEqual(rendered, u'(first.en.html)')
Пример #41
0
 def finalize_subject(self):
     step = self.steps[u'paper']
     return squeeze(render_to_string(step.subject_template, step.context(dict(finalize=True))))
Пример #42
0
 def test_trim(self):
     self.assertEqual(squeeze(u'  left'), u'left')
     self.assertEqual(squeeze(u'right      '), u'right')
     self.assertEqual(squeeze(u' both '), u'both')
     self.assertEqual(squeeze(u'\n\n\n111\n\n\n'), u'111')
     self.assertEqual(squeeze(u'    aaa fff    rrr   '), u'aaa fff rrr')
Пример #43
0
 def test_linebreaks_and_tabs(self):
     self.assertEqual(squeeze(u'\n\n1\t2\r\r3\r\n4\n\r5\r\r6\n\n7'), u'1 2 3 4 5 6 7')
     self.assertEqual(squeeze(u'1 \n2\n\n 3     \t   4\n\n\n\n\n\n\n\n\n5'), u'1 2 3 4 5')
Пример #44
0
 def test_empty_string(self):
     self.assertEqual(squeeze(u''), u'')
Пример #45
0
 def test_long_random_text(self):
     sample = u''.join(random.choice(string.printable if random.random() < 0.5 else string.whitespace)
             for i in range(1000))
     res = squeeze(sample)
     self.assertRegexpMatches(res, r'^(\S+ )*\S+$')
Пример #46
0
 def test_translated_template_has_priority(self):
     # Existing: first.html, first.en.html
     with translation(u'en'):
         rendered = squeeze(render_to_string(u'first.html'))
         self.assertEqual(rendered, u'(first.en.html)')
Пример #47
0
 def test_with_only_untranslated_template(self):
     # Existing: first.html
     # Missing: first.de.html
     with translation(u'de'):
         rendered = squeeze(render_to_string(u'first.html'))
         self.assertEqual(rendered, u'(first.html)')
Пример #48
0
class Recipient(FormatMixin, models.Model):
    # May NOT be NULL
    message = models.ForeignKey(u'Message')

    # May be empty
    name = models.CharField(blank=True,
                            max_length=255,
                            help_text=escape(
                                squeeze(u"""
                Recipient full name. For instance setting name to "John Smith" and e-mail to
                "*****@*****.**" will send the message to "John Smith <*****@*****.**>".
                """)))

    # Should NOT be empty
    mail = models.EmailField(
        max_length=255,
        help_text=u'Recipient e-mail address, e.g. "*****@*****.**".')

    # May NOT be NULL
    TYPES = FieldChoices(
        (u'TO', 1, _(u'mail:Recipient:type:TO')),
        (u'CC', 2, _(u'mail:Recipient:type:CC')),
        (u'BCC', 3, _(u'mail:Recipient:type:BCC')),
    )
    type = models.SmallIntegerField(
        choices=TYPES._choices, help_text=u'Recipient type: To, Cc, or Bcc.')

    # May NOT be NULL
    STATUSES = FieldChoices(
        # For inbound messages
        (u'INBOUND', 8, _(u'mail:Recipient:status:INBOUND')),
        # For outbound messages
        (u'UNDEFINED', 1, _(u'mail:Recipient:status:UNDEFINED')),
        (u'QUEUED', 2, _(u'mail:Recipient:status:QUEUED')),
        (u'REJECTED', 3, _(u'mail:Recipient:status:REJECTED')),
        (u'INVALID', 4, _(u'mail:Recipient:status:INVALID')),
        (u'SENT', 5, _(u'mail:Recipient:status:SENT')),
        (u'DELIVERED', 6, _(u'mail:Recipient:status:DELIVERED')),
        (u'OPENED', 7, _(u'mail:Recipient:status:OPENED')),
    )
    INBOUND_STATUSES = (STATUSES.INBOUND, )
    OUTBOUND_STATUSES = (
        STATUSES.UNDEFINED,
        STATUSES.QUEUED,
        STATUSES.REJECTED,
        STATUSES.INVALID,
        STATUSES.SENT,
        STATUSES.DELIVERED,
        STATUSES.OPENED,
    )
    status = models.SmallIntegerField(choices=STATUSES._choices,
                                      help_text=squeeze(u"""
                Delivery status for the message recipient. It must be "Inbound" for inbound mesages
                or one of the remaining statuses for outbound messages.
                """))

    # May be empty
    status_details = models.CharField(blank=True,
                                      max_length=255,
                                      help_text=squeeze(u"""
                Unspecific delivery status details set by e-mail transport. Leave blank if not
                sure.
                """))

    # May be empty
    remote_id = models.CharField(blank=True,
                                 max_length=255,
                                 db_index=True,
                                 help_text=squeeze(u"""
                Recipient reference ID set by e-mail transport. Leave blank if not sure.
                """))

    # Backward relations added to other models:
    #
    #  -- Message.recipient_set
    #     Should NOT be empty

    # Indexes:
    #  -- message:   ForeignKey
    #  -- remote_id: on field

    objects = RecipientQuerySet.as_manager()

    @property
    def formatted(self):
        return formataddr((self.name, self.mail))

    @formatted.setter
    def formatted(self, value):
        self.name, self.mail = parseaddr(value)

    def __unicode__(self):
        return u'[{}] {}'.format(self.pk, self.mail)
Пример #49
0
class Message(FormatMixin, models.Model):
    # May NOT be NULL
    TYPES = FieldChoices(
        (u'INBOUND', 1, _(u'mail:Message:type:INBOUND')),
        (u'OUTBOUND', 2, _(u'mail:Message:type:OUTBOUND')),
    )
    type = models.SmallIntegerField(choices=TYPES._choices)

    # May NOT be NULL
    created = models.DateTimeField(auto_now_add=True,
                                   help_text=squeeze(u"""
                Date and time the message object was created,
                """))

    # NOT NULL for processed messages; NULL for queued messages
    processed = models.DateTimeField(blank=True,
                                     null=True,
                                     help_text=squeeze(u"""
                Date and time the message was sent or received and processed. Leave blank if you
                want the application to process it.
                """))

    # May be empty
    from_name = models.CharField(blank=True,
                                 max_length=255,
                                 help_text=escape(
                                     squeeze(u"""
                Sender full name. For instance setting name to "John Smith" and e-mail to
                "*****@*****.**" will set the sender address to "John Smith <*****@*****.**>".
                """)))

    # Should NOT be empty
    from_mail = models.EmailField(max_length=255,
                                  help_text=squeeze(u"""
                Sender e-mail address, e.g. "*****@*****.**".
                """))

    # May be empty for inbound messages; Empty for outbound messages
    received_for = models.EmailField(blank=True,
                                     max_length=255,
                                     help_text=squeeze(u"""
                The address we received the massage for. It may, but does not have to be among the
                message recipients, as the address may have heen bcc-ed to. The address is empty
                for all outbound messages. It may also be empty for inbound messages if we don't
                know it, or the used mail transport does not support it.
                """))

    # May be empty
    subject = models.CharField(blank=True, max_length=255)

    # May be empty
    text = models.TextField(blank=True,
                            help_text=squeeze(u"""
                "text/plain" message body alternative.
                """))
    html = models.TextField(blank=True,
                            help_text=squeeze(u"""
                "text/html" message body alternative.
                """))

    # Dict: String->(String|[String]); May be empty
    headers = JSONField(blank=True,
                        default={},
                        help_text=squeeze(u"""
                Dictionary mapping header names to their values, or lists of their values. For
                outbound messages it contains only extra headers added by the sender. For inbound
                messages it contains all message headers.
                """))

    # May be empty; Backward generic relation
    attachment_set = generic.GenericRelation(
        u'attachments.Attachment',
        content_type_field=u'generic_type',
        object_id_field=u'generic_id')

    # Backward relations:
    #
    #  -- recipient_set: by Recipient.message
    #     Should NOT be empty

    # Indexes:
    #  -- processed, id: index_together
    #  -- created, id:   index_together

    objects = MessageQuerySet.as_manager()

    class Meta:
        index_together = [
            [u'processed', u'id'],
            [u'created', u'id'],
        ]

    @property
    def from_formatted(self):
        return formataddr((self.from_name, self.from_mail))

    @from_formatted.setter
    def from_formatted(self, value):
        self.from_name, self.from_mail = parseaddr(value)

    @staticmethod
    def prefetch_attachments(path=None, queryset=None):
        u"""
        Use to prefetch ``Message.attachments``.
        """
        if queryset is None:
            queryset = Attachment.objects.get_queryset()
        queryset = queryset.order_by_pk()
        return Prefetch(join_lookup(path, u'attachment_set'),
                        queryset,
                        to_attr=u'attachments')

    @cached_property
    def attachments(self):
        u"""
        Cached list of all message attachments ordered by ``pk``. May be prefetched with
        ``prefetch_related(Message.prefetch_attachments())`` queryset method.
        """
        return list(self.attachment_set.order_by_pk())

    @staticmethod
    def prefetch_recipients(path=None, queryset=None):
        u"""
        Use to prefetch ``Message.recipients``.
        """
        if queryset is None:
            queryset = Recipient.objects.get_queryset()
        queryset = queryset.order_by_pk()
        return Prefetch(join_lookup(path, u'recipient_set'),
                        queryset,
                        to_attr=u'recipients')

    @cached_property
    def recipients(self):
        u"""
        Cached list of all message recipients ordered by ``pk``. May be prefetched with
        ``prefetch_related(Message.prefetch_recipients())`` queryset method.
        """
        return list(self.recipient_set.order_by_pk())

    @cached_property
    def recipients_to(self):
        u"""
        Cached list of all message "to" recipients ordered by ``pk``. Takes advantage of
        ``Message.recipients`` if it is already fetched.
        """
        if u'recipients' in self.__dict__:
            return list(r for r in self.recipients
                        if r.type == Recipient.TYPES.TO)
        else:
            return list(self.recipient_set.to().order_by_pk())

    @cached_property
    def recipients_cc(self):
        u"""
        Cached list of all message "cc" recipients ordered by ``pk``. Takes advantage of
        ``Message.recipients`` if it is already fetched.
        """
        if u'recipients' in self.__dict__:
            return list(r for r in self.recipients
                        if r.type == Recipient.TYPES.CC)
        else:
            return list(self.recipient_set.cc().order_by_pk())

    @cached_property
    def recipients_bcc(self):
        u"""
        Cached list of all message "bcc" recipients ordered by ``pk``. Takes advantage of
        ``Message.recipients`` if it is already fetched.
        """
        if u'recipients' in self.__dict__:
            return list(r for r in self.recipients
                        if r.type == Recipient.TYPES.BCC)
        else:
            return list(self.recipient_set.bcc().order_by_pk())

    @cached_property
    def to_formatted(self):
        return u', '.join(r.formatted for r in self.recipients_to)

    @cached_property
    def cc_formatted(self):
        return u', '.join(r.formatted for r in self.recipients_cc)

    @cached_property
    def bcc_formatted(self):
        return u', '.join(r.formatted for r in self.recipients_bcc)

    def __unicode__(self):
        return format(self.pk)
Пример #50
0
    def __init__(self, page, create, *args, **kwargs):
        super(PageEditForm, self).__init__(*args, **kwargs)
        self.page = page
        self.create = create

        edit_root = not create and page.is_root
        edit_redirect = not create and page.is_redirect
        edit_regular = not create and not page.is_root and not page.is_redirect
        assert bool(create) + bool(edit_root) + bool(edit_redirect) + bool(edit_regular) == 1

        self.fieldsets = [
                Bunch(label=None, collapse=False, fields=[]),
                Bunch(label=u'Raw Config', collapse=True, fields=[]),
                Bunch(label=u'Page Content', collapse=False, fields=[]),
                ]

        if edit_regular:
            self.fields[u'parent'] = forms.CharField(
                label=u'URL Path',
                validators=[
                    RegexValidator(pages.path_regex, squeeze(u"""
                        Enter a valid path. It must be an absolute path with respect to the root
                        page starting and ending with a slash.
                        """)),
                    ],
                widget=forms.TextInput(attrs={
                    u'class': u'popup-path',
                    u'style': u'width: 50em;',
                    u'data-popup-url': reverse(u'admin:pages_index', args=[page.lang]),
                    u'data-icon': staticfiles_storage.url(u'admin/img/selector-search.gif'),
                    }),
                )
            self.initial[u'parent'] = page.ppath
            self.fieldsets[0].fields.append(self[u'parent'])
            self.fieldsets[0].fields.append(LivePath(page.lang, self[u'parent']))
        else:
            self.fieldsets[0].fields.append(FakeField(u'URL Path', page.path))

        if create or edit_regular:
            self.fields[u'name'] = forms.CharField(
                label=u'URL Name',
                validators=[
                    RegexValidator(pages.slug_regex,
                        u'Enter a valid slug. Only letters, numbers and dashes are allowed.'),
                    ],
                )
            if not create:
                self.initial[u'name'] = page.name
            self.fieldsets[0].fields.append(self[u'name'])

        if create or edit_root or edit_regular:
            self.fields[u'title'] = forms.CharField(
                label=u'Page Title',
                required=False,
                widget=forms.TextInput(attrs={
                    u'style': u'width: 50em;',
                    }),
                )
            if not create:
                self.initial[u'title'] = page._config.get(u'title')
            self.fieldsets[0].fields.append(self[u'title'])

        if create or edit_root or edit_regular:
            self.fields[u'label'] = forms.CharField(
                label=u'Menu Label',
                required=False,
                )
            if not create:
                self.initial[u'label'] = page._config.get(u'label')
            self.fieldsets[0].fields.append(self[u'label'])

        if create or edit_regular:
            self.fields[u'order'] = forms.CharField(
                label=u'Sort Key',
                required=False,
                )
            if not create:
                self.initial[u'order'] = page._config.get(u'order')
            self.fieldsets[0].fields.append(self[u'order'])

        if create or edit_regular:
            for lang, _ in settings.LANGUAGES:
                if lang != page.lang:
                    key = u'lang_{}'.format(lang)
                    self.fields[key] = forms.CharField(
                        label=u'Translation {}'.format(lang.upper()),
                        required=False,
                        validators=[
                            RegexValidator(pages.path_regex, squeeze(u"""
                                Enter a valid path. It must be an absolute path with respect to the
                                root page starting and ending with a slash.
                                """)),
                            ],
                        widget=forms.TextInput(attrs={
                            u'class': u'popup-path',
                            u'style': u'width: 50em;',
                            u'data-popup-url': reverse(u'admin:pages_index', args=[lang]),
                            u'data-icon': staticfiles_storage.url(u'admin/img/selector-search.gif'),
                            }),
                        )
                    if not create:
                        self.initial[key] = page._config.get(key)
                    self.fieldsets[0].fields.append(self[key])
                    self.fieldsets[0].fields.append(LivePath(lang, self[key]))

        if edit_redirect:
            self.fields[u'redirect'] = forms.CharField(
                label=u'Redirect',
                validators=[
                    RegexValidator(pages.path_regex, squeeze(u"""
                        Enter a valid path. It must be an absolute path with respect to the root
                        page starting and ending with a slash.
                        """)),
                    ],
                widget=forms.TextInput(attrs={
                    u'class': u'popup-path',
                    u'style': u'width: 50em;',
                    u'data-popup-url': reverse(u'admin:pages_index', args=[page.lang]),
                    u'data-icon': staticfiles_storage.url(u'admin/img/selector-search.gif'),
                    }),
                )
            self.initial[u'redirect'] = page.redirect_path
            self.fieldsets[0].fields.append(self[u'redirect'])
            self.fieldsets[0].fields.append(LivePath(page.lang, self[u'redirect']))

        if create or edit_root or edit_regular:
            self.fields[u'disabled'] = forms.BooleanField(
                label=u'Disabled',
                required=False,
                )
            if not create:
                self.initial[u'disabled'] = bool(page._config.get(u'disabled'))
            self.fieldsets[0].fields.append(self[u'disabled'])

        if create or edit_root or edit_regular:
            self.fields[u'raw'] = forms.CharField(
                label=u'',
                required=False,
                widget=forms.Textarea(attrs={
                    u'style': u'width: 100%; height: 10em;',
                    }),
                )
            if not create:
                self.initial[u'raw'] = page.raw_config
            self.fieldsets[1].fields.append(self[u'raw'])

        if create or edit_root or edit_regular:
            with translation(page.lang):
                url = reverse(u'admin:pages_preview')
            self.fields[u'template'] = forms.CharField(
                label=u'',
                required=False,
                widget=forms.Textarea(attrs={
                    u'class': u'template-widget',
                    u'data-url': url,
                    }),
                )
            if not create:
                self.initial[u'template'] = page.template
            self.fieldsets[2].fields.append(self[u'template'])