Example #1
0
class _EntityEmailsSendType(JobType):
    id = JobType.generate_id('emails', 'entity_emails_send')
    verbose_name = _('Send entity emails')
    periodic = JobType.PSEUDO_PERIODIC

    def _execute(self, job):
        Status = EntityEmail.Status

        for email in EntityEmail.objects.exclude(is_deleted=True).filter(
                # status__in=[MAIL_STATUS_NOTSENT, MAIL_STATUS_SENDINGERROR],
                status__in=[Status.NOT_SENT, Status.SENDING_ERROR], ):
            email.send()

    # We have to implement it because it is a PSEUDO_PERIODIC JobType
    def next_wakeup(self, job, now_value):
        Status = EntityEmail.Status
        filter_mail = EntityEmail.objects.exclude(is_deleted=True).filter

        # if filter_mail(status=MAIL_STATUS_NOTSENT).exists():
        if filter_mail(status=Status.NOT_SENT).exists():
            return now_value

        # if filter_mail(status=MAIL_STATUS_SENDINGERROR).exists():
        if filter_mail(status=Status.SENDING_ERROR).exists():
            return now_value + timedelta(minutes=ENTITY_EMAILS_RETRY)

        return None
Example #2
0
class _GenerateDocsType(JobType):
    id = JobType.generate_id('recurrents', 'generate_docs')
    verbose_name = _('Generate recurrent documents')
    periodic = JobType.PSEUDO_PERIODIC

    # TODO: we could add a field RecurrentGenerator.next_generation
    #  => queries would be more efficient
    def _get_generators(self, now_value):
        return get_rgenerator_model().objects \
            .filter(is_working=True) \
            .filter(Q(last_generation__isnull=True,
                      first_generation__lte=now_value,
                      ) |
                    Q(last_generation__isnull=False)
                    )

    def _execute(self, job):
        # TODO: test is_working VS delete it (see next_wakeup() && job refreshing too)
        for generator in self._get_generators(now()):
            last = generator.last_generation
            next_generation = (generator.first_generation if last is None else
                               last + generator.periodicity.as_timedelta())

            if next_generation <= now():
                with atomic():
                    template = generator.template.get_real_entity()

                    template.create_entity()

                    generator.last_generation = next_generation
                    generator.save()

    # TODO: with docs generate the last time ?? (but stats will be cleaned at
    #       next run, even if nothing is generated...)
    # def get_stats(self, job):
    #     count = JobResult.objects.filter(job=job, raw_errors__isnull=True).count()
    #
    #     return [ungettext('%s entity has been successfully modified.',
    #                       '%s entities have been successfully modified.',
    #                       count
    #                      ) % count,
    #            ]

    # We have to implement it because it is a PSEUDO_PERIODIC JobType
    def next_wakeup(self, job, now_value):
        wakeup = None

        for generator in self._get_generators(now_value):
            last = generator.last_generation

            if last is None:  # We are sure that first_generation < now_value
                wakeup = now_value
                break

            recurrent_date = last + generator.periodicity.as_timedelta()
            wakeup = recurrent_date if not wakeup else min(
                wakeup, recurrent_date)

        return wakeup
Example #3
0
 def _create_invalid_job(self, user=None, status=Job.STATUS_WAIT):
     return Job.objects.create(
         user=user or self.user,
         type_id=JobType.generate_id('creme_core', 'invalid'),
         language='en',
         status=status,
         raw_data='[]',
     )
Example #4
0
class _UserMessagesSendType(JobType):
    id           = JobType.generate_id('assistants', 'usermessages_send')
    verbose_name = _(u'Send usermessages emails')
    periodic     = JobType.PSEUDO_PERIODIC

    def _execute(self, job):
        UserMessage.send_mails(job)

    # We have to implement it because it is a PSEUDO_PERIODIC JobType
    def next_wakeup(self, job, now_value):
        if UserMessage.objects.filter(email_sent=False).exists():
            return now_value
Example #5
0
class _CampaignEmailsSendType(JobType):
    id = JobType.generate_id('emails', 'campaign_emails_send')
    verbose_name = _('Send emails from campaigns')
    periodic = JobType.PSEUDO_PERIODIC

    def _execute(self, job):
        for sending in EmailSending.objects.exclude(
                campaign__is_deleted=True,
        ).exclude(
                # state=SENDING_STATE_DONE,
                state=EmailSending.State.DONE,
        ).filter(
                # Q(type=SENDING_TYPE_IMMEDIATE) | Q(sending_date__lte=now())
                Q(type=EmailSending.Type.IMMEDIATE)
                | Q(sending_date__lte=now())):
            # sending.state = SENDING_STATE_INPROGRESS
            sending.state = EmailSending.State.IN_PROGRESS
            sending.save()

            # if getattr(settings, 'REMOTE_STATS', False):
            #     from creme.emails.utils.remoteutils import populate_minicreme #broken
            #     populate_minicreme(sending)

            status = sending.send_mails()

            # TODO: move in send_mails() ???
            # sending.state = status or SENDING_STATE_DONE
            sending.state = status or EmailSending.State.DONE
            sending.save()

    # We have to implement it because it is a PSEUDO_PERIODIC JobType
    def next_wakeup(self, job, now_value):
        qs = EmailSending.objects.exclude(campaign__is_deleted=True, ).exclude(
            state=EmailSending.State.DONE)  # state=SENDING_STATE_DONE

        # if qs.filter(type=SENDING_TYPE_IMMEDIATE).exists():
        if qs.filter(type=EmailSending.Type.IMMEDIATE).exists():
            return now_value

        # dsending = qs.filter(type=SENDING_TYPE_DEFERRED).order_by('sending_date').first()
        dsending = qs.filter(
            type=EmailSending.Type.DEFERRED).order_by('sending_date').first()

        return dsending.sending_date if dsending is not None else None
Example #6
0
class _CruditySynchronizeType(JobType):
    id           = JobType.generate_id('crudity', 'synchronization')
    verbose_name = _('Synchronize externals data sent to Creme')
    periodic     = JobType.PERIODIC

    def _execute(self, job):
        try:
            user = CremeUser.objects.get(pk=job.data['user'])
        except CremeUser.DoesNotExist:
            JobResult.objects.create(
                job=job,
                messages=[
                    gettext("The configured default user is invalid. "
                            "Edit the job's configuration to fix it."
                           ),
                ],
            )

            user = CremeUser.objects.get_admin()

        from . import registry

        count = len(registry.crudity_registry.fetch(user))
        JobResult.objects.create(
            job=job,
            messages=[
                ngettext('There is {count} change',
                         'There are {count} changes',
                         count
                        ).format(count=count),
            ],
        )

    def get_config_form_class(self, job):
        from .forms import CruditySynchronizeJobForm
        return CruditySynchronizeJobForm

    @property
    def results_bricks(self):
        from creme.creme_core.bricks import JobResultsBrick
        return [JobResultsBrick()]
Example #7
0
class _ActiveSyncType(JobType):
    id = JobType.generate_id('activesync', 'synchronise')
    verbose_name = _(u'ActiveSync synchronisation')
    periodic = JobType.PERIODIC

    def _execute(self, job):
        for user in get_user_model().objects.all():
            try:
                Synchronization(user).synchronize()
            except CremeActiveSyncError as e:
                JobResult.objects.create(
                    job=job,
                    messages=[
                        ugettext(u'An error occurred for the user «%s»') %
                        user,
                        ugettext(u'Original error: %s') % e,
                    ],
                )

    def get_description(self, job):
        return [ugettext(u'Synchronise data with the ActiveSync server')]
Example #8
0
class _ComApproachesEmailsSendType(JobType):
    id = JobType.generate_id('commercial', 'com_approaches_emails_send')
    verbose_name = _('Send emails for commercials approaches')

    # It would be too difficult/inefficient to compute the next wake up,
    # so it is not PSEUDO_PERIODIC.
    periodic = JobType.PERIODIC

    # TODO: add a config form which stores the rules in job.data
    list_target_orga = [(REL_SUB_CUSTOMER_SUPPLIER, 30)]

    def _execute(self, job):
        from creme import persons
        from creme.opportunities import get_opportunity_model
        from creme.opportunities.constants import REL_SUB_TARGETS

        from .models import CommercialApproach

        Organisation = persons.get_organisation_model()
        Contact = persons.get_contact_model()
        Opportunity = get_opportunity_model()

        emails = []

        get_ct = ContentType.objects.get_for_model
        ct_orga = get_ct(Organisation)
        ct_contact = get_ct(Contact)
        ct_opp = get_ct(Opportunity)

        now_value = now()
        managed_orga_ids = [
            *Organisation.objects.filter(is_managed=True).values_list(
                'id', flat=True),
        ]
        opp_filter = Opportunity.objects.filter

        EMAIL_SENDER = settings.EMAIL_SENDER

        for rtype, delay in self.list_target_orga:
            com_apps_filter = CommercialApproach.objects.filter(
                creation_date__gt=now_value - timedelta(days=delay), ).filter

            # TODO: are 'values_list' real optimizations here ??
            #       ==> remove them when CommercialApproach use real ForeignKey
            for orga in Organisation.objects.filter(
                    is_managed=False,
                    relations__type=rtype,
                    relations__object_entity__in=managed_orga_ids,
            ):
                if com_apps_filter(entity_content_type=ct_orga,
                                   entity_id=orga.id).exists():
                    continue

                if com_apps_filter(
                        entity_content_type=ct_contact,
                        entity_id__in=orga.get_managers().values_list(
                            'id', flat=True),
                ).exists():
                    continue

                if com_apps_filter(
                        entity_content_type=ct_contact,
                        entity_id__in=orga.get_employees().values_list(
                            'id', flat=True),
                ).exists():
                    continue

                if com_apps_filter(
                        entity_content_type=ct_opp,
                        entity_id__in=opp_filter(
                            relations__type=REL_SUB_TARGETS,
                            relations__object_entity=orga,
                        ).values_list('id', flat=True),
                ).exists():
                    continue

                emails.append(
                    EmailMessage(
                        gettext(
                            '[CremeCRM] The organisation «{}» seems neglected'
                        ).format(orga),
                        gettext(
                            "It seems you haven't created a commercial approach for "
                            "the organisation «{orga}» since {delay} days.").
                        format(
                            orga=orga,
                            delay=delay,
                        ),
                        EMAIL_SENDER,
                        [orga.user.email],
                    ))

        # TODO: factorise jobs which send emails
        if emails:
            try:
                with get_connection() as connection:
                    connection.send_messages(emails)
            except Exception as e:
                JobResult.objects.create(
                    job=job,
                    messages=[
                        gettext('An error has occurred while sending emails'),
                        gettext('Original error: {}').format(e),
                    ],
                )

    def get_description(self, job):
        return [
            gettext(
                "For each customer organisation, an email is sent to its owner "
                "(ie: a Creme user), if there is no commercial approach since "
                "{} days linked to: the organisation, one of its managers/employees, "
                "or an Opportunity which targets this organisation.").format(
                    self.list_target_orga[0][1]),
            gettext(
                "Hint: to create commercial approaches, activate the field "
                "«Is a commercial approach?» in the configuration of Activities' forms ; "
                "so when you create an Activity, if you check the box, some approaches "
                "will be created for participants, subjects & linked entities."
            ),
            gettext(
                "Hint: to see commercial approaches, activate the related block "
                "on detail-views."),
        ]
Example #9
0
 class TestJobType1(JobType):
     id = JobType.generate_id('creme_core', 'test1')