Exemple #1
0
    def run_daily_activities(self):
        # If gengosystem is disabled, we don't want to do anything.
        if not waffle.switch_is_active('gengosystem'):
            return

        gengo_api = FjordGengo()

        if not gengo_api.is_configured():
            # If Gengo isn't configured, then we drop out here rather
            # than raise a GengoConfig error.
            return

        balance = gengo_api.get_balance()
        threshold = settings.GENGO_ACCOUNT_BALANCE_THRESHOLD

        if threshold < balance < (2 * threshold):
            mail_admins(
                subject='Warning: Gengo account balance {0} < {1}'.format(
                    balance, 2 * threshold),
                message=wrap_with_paragraphs(
                    'Dear mom,'
                    '\n\n'
                    'Translations are the fab. Running low on funds. Send '
                    'more money when you get a chance.'
                    '\n\n'
                    'Love,'
                    '\n\n'
                    'Fjord McGengo'
                )
            )
Exemple #2
0
    def run_daily_activities(self):
        # If gengosystem is disabled, we don't want to do anything.
        if not waffle.switch_is_active('gengosystem'):
            return

        gengo_api = FjordGengo()

        if not gengo_api.is_configured():
            # If Gengo isn't configured, then we drop out here rather
            # than raise a GengoConfig error.
            return

        balance = gengo_api.get_balance()
        threshold = settings.GENGO_ACCOUNT_BALANCE_THRESHOLD

        if threshold < balance < (2 * threshold):
            mail_admins(
                subject='Warning: Gengo account balance {0} < {1}'.format(
                    balance, 2 * threshold),
                message=wrap_with_paragraphs(
                    'Dear mom,'
                    '\n\n'
                    'Translations are the fab. Running low on funds. Send '
                    'more money when you get a chance.'
                    '\n\n'
                    'Love,'
                    '\n\n'
                    'Fjord McGengo'
                )
            )
Exemple #3
0
    def pull_translations(self):
        # If gengosystem is disabled, we just return immediately. We
        # can backfill later.
        if not waffle.switch_is_active('gengosystem'):
            return

        gengo_api = FjordGengo()

        if not gengo_api.is_configured():
            # If Gengo isn't configured, then we drop out here rather
            # than raise a GengoConfig error.
            return

        # Get all the orders that are in progress
        orders = GengoOrder.objects.filter(status=STATUS_IN_PROGRESS)
        for order in orders:
            # Get the list of all completed jobs
            completed = gengo_api.completed_jobs_for_order(order.order_id)

            # If there are no completed jobs, then we don't need to
            # bother doing any additional processing for this order
            if not completed:
                continue

            # For each complete job we haven't seen before, pull it
            # from the db, save the translated text and update all the
            # bookkeeping.
            for comp in completed:
                id_ = GengoJob.unique_id_to_id(comp['custom_data'])

                job = GengoJob.objects.get(pk=id_)
                if job.status == STATUS_COMPLETE:
                    continue

                instance = job.content_object
                setattr(instance, job.dst_field, comp['body_tgt'])
                instance.save()

                job.mark_complete()

            # Check to see if there are still outstanding jobs for
            # this order. If there aren't, close the order out.
            outstanding = (GengoJob.objects
                           .filter(order=order, status=STATUS_IN_PROGRESS)
                           .count())

            if outstanding == 0:
                order.mark_complete()
Exemple #4
0
    def pull_translations(self):
        # If gengosystem is disabled, we just return immediately. We
        # can backfill later.
        if not waffle.switch_is_active('gengosystem'):
            return

        gengo_api = FjordGengo()

        if not gengo_api.is_configured():
            # If Gengo isn't configured, then we drop out here rather
            # than raise a GengoConfig error.
            return

        # Get all the orders that are in progress
        orders = GengoOrder.objects.filter(status=STATUS_IN_PROGRESS)
        for order in orders:
            # Get the list of all completed jobs
            completed = gengo_api.completed_jobs_for_order(order.order_id)

            # If there are no completed jobs, then we don't need to
            # bother doing any additional processing for this order
            if not completed:
                continue

            # For each complete job we haven't seen before, pull it
            # from the db, save the translated text and update all the
            # bookkeeping.
            for comp in completed:
                id_ = GengoJob.unique_id_to_id(comp['custom_data'])

                job = GengoJob.objects.get(pk=id_)
                if job.status == STATUS_COMPLETE:
                    continue

                instance = job.content_object
                setattr(instance, job.dst_field, comp['body_tgt'])
                instance.save()

                job.mark_complete()

            # Check to see if there are still outstanding jobs for
            # this order. If there aren't, close the order out.
            outstanding = (GengoJob.objects
                           .filter(order=order, status=STATUS_IN_PROGRESS)
                           .count())

            if outstanding == 0:
                order.mark_complete()
Exemple #5
0
def gengo_translator_view(request):
    gengo_machiners = Product.objects.filter(translation_system='gengo_machine')
    balance = None
    configured = False

    if settings.GENGO_PUBLIC_KEY and settings.GENGO_PRIVATE_KEY:
        gengo_api = FjordGengo()
        balance = gengo_api.get_balance()
        configured = True

    return render(request, 'admin/gengo_translator_view.html', {
        'configured': configured,
        'settings': settings,
        'gengo_machiners': gengo_machiners,
        'balance': balance
    })
Exemple #6
0
def gengo_translator_view(request):
    gengo_machiners = Product.objects.filter(
        translation_system='gengo_machine')
    balance = None
    configured = False

    if settings.GENGO_PUBLIC_KEY and settings.GENGO_PRIVATE_KEY:
        gengo_api = FjordGengo()
        balance = gengo_api.get_balance()
        configured = True

    return render(
        request, 'admin/gengo_translator_view.html', {
            'configured': configured,
            'settings': settings,
            'gengo_machiners': gengo_machiners,
            'balance': balance
        })
Exemple #7
0
    def run_daily_activities(self):
        # If gengosystem is disabled, we don't want to do anything.
        if not waffle.switch_is_active('gengosystem'):
            return

        gengo_api = FjordGengo()

        if not gengo_api.is_configured():
            # If Gengo isn't configured, then we drop out here rather
            # than raise a GengoConfig error.
            return

        balance = gengo_api.get_balance()
        threshold = settings.GENGO_ACCOUNT_BALANCE_THRESHOLD

        if threshold < balance < (2 * threshold):
            recipients = get_recipients('gengo_balance')
            subject = 'Warning: Gengo account balance {0} < {1}'.format(
                balance, 2 * threshold)
            body = wrap_with_paragraphs(dedent("""\
            Dear mom,

            Translations are the fab. Running low on funds. Send
            more money when you get a chance.

            Love,

            Fjord McGengo
            """))

            if recipients:
                send_mail(
                    subject=subject,
                    message=body,
                    recipient_list=recipients,
                    from_email=settings.SERVER_EMAIL
                )

            else:
                log.info('No recipients for "%s"\n%s\n%s' % (
                    'gengo_balance', subject, body))
Exemple #8
0
    def run_daily_activities(self):
        # If gengosystem is disabled, we don't want to do anything.
        if not waffle.switch_is_active('gengosystem'):
            return

        gengo_api = FjordGengo()

        if not gengo_api.is_configured():
            # If Gengo isn't configured, then we drop out here rather
            # than raise a GengoConfig error.
            return

        balance = gengo_api.get_balance()
        threshold = settings.GENGO_ACCOUNT_BALANCE_THRESHOLD

        if threshold < balance < (2 * threshold):
            recipients = get_recipients('gengo_balance')
            subject = 'Warning: Gengo account balance {0} < {1}'.format(
                balance, 2 * threshold)
            body = wrap_with_paragraphs(
                dedent("""\
            Dear mom,

            Translations are the fab. Running low on funds. Send
            more money when you get a chance.

            Love,

            Fjord McGengo
            """))

            if recipients:
                send_mail(subject=subject,
                          message=body,
                          recipient_list=recipients,
                          from_email=settings.SERVER_EMAIL)

            else:
                log.info('No recipients for "%s"\n%s\n%s' %
                         ('gengo_balance', subject, body))
Exemple #9
0
def gengo_translator_view(request):
    """Covers Gengo-specific translation system status"""
    products = Product.objects.all()
    balance = None
    configured = False
    gengo_languages = None
    missing_prod_locales = None

    if settings.GENGO_PUBLIC_KEY and settings.GENGO_PRIVATE_KEY:
        gengo_api = FjordGengo()
        balance = gengo_api.get_balance()
        configured = True

        # Figure out the list of languages Gengo supports and relate them
        # to PROD locales.
        languages = sorted(gengo_api.get_languages(raw=True)['response'],
                           key=lambda item: item['lc'])
        gengo_languages = []
        PROD_LANG = settings.PROD_LANGUAGES
        for lang in languages:
            lang_lc = lang['lc']
            prod_langs = [item for item in PROD_LANG
                          if locale_equals_language(item, lang_lc)]
            prod_langs = ' '.join(prod_langs)

            gengo_languages.append(
                (lang_lc, lang['language'], prod_langs)
            )

        # Figure out the list of PROD locales that don't have a Gengo
        # supported language. (Yes, this conflates locale with
        # language which isn't great, but is good enough for now.)
        languages = gengo_api.get_languages()
        missing_prod_locales = []
        for prod_lang in PROD_LANG:
            langs = [item for item in languages
                     if locale_equals_language(prod_lang, item)]
            if langs:
                continue
            missing_prod_locales.append(prod_lang)

        # How many orders have we created/completed in the last week
        # day-by-day?
        seven_days = datetime.now() - timedelta(days=7)

        orders = GengoOrder.objects.filter(created__gte=seven_days)
        created_by_day = {}
        for order in orders:
            dt = order.created.strftime('%Y-%m-%d')
            created_by_day.setdefault(dt, []).append(order)

        orders = GengoOrder.objects.filter(completed__gte=seven_days)
        completed_by_day = {}
        for order in orders:
            dt = order.completed.strftime('%Y-%m-%d')
            completed_by_day.setdefault(dt, []).append(order)

        # Get date labels in YYYY-mm-dd form for the last 7 days
        days = [
            (datetime.now() - timedelta(days=i)).strftime('%Y-%m-%d')
            for i in range(7)
        ]

        seven_days_of_orders = []
        for day in sorted(days):
            seven_days_of_orders.append(
                (day,
                 len(created_by_day.get(day, [])),
                 len(completed_by_day.get(day, [])))
            )

        outstanding = [
            {
                'id': order.id,
                'order_id': order.order_id,
                'created': order.created,
                'total_jobs': order.gengojob_set.all().count(),
                'completed_jobs': order.completed_jobs().count(),
                'outstanding_jobs': order.outstanding_jobs().count(),
            }
            for order in GengoOrder.objects.filter(completed__isnull=True)]

    return render(request, 'admin/gengo_translator_view.html', {
        'title': 'Translations - Gengo Maintenance',
        'configured': configured,
        'settings': settings,
        'products': products,
        'outstanding': outstanding,
        'seven_days_of_orders': seven_days_of_orders,
        'balance': balance,
        'gengo_languages': gengo_languages,
        'missing_prod_locales': missing_prod_locales,
    })
Exemple #10
0
    def push_translations(self):
        # If gengosystem is disabled, we just return immediately. We
        # can backfill later.
        if not waffle.switch_is_active('gengosystem'):
            return

        gengo_api = FjordGengo()

        if not gengo_api.is_configured():
            # If Gengo isn't configured, then we drop out here rather
            # than raise a GengoConfig error.
            return

        if self.gengo_watch_balance:
            balance = gengo_api.get_balance()
            threshold = settings.GENGO_ACCOUNT_BALANCE_THRESHOLD

            # statsd the balance so we can track it with graphite.
            statsd.gauge('translation.gengo.balance', balance)

            if not self.balance_good_to_continue(balance, threshold):
                # If we don't have enough balance, stop.
                return

        # Create language buckets for the jobs for this translator.
        # We bucket by language because this makes it easier for a
        # single Gengo translator to translate all the jobs in an
        # order.
        jobs = GengoJob.objects.filter(
            tier=self.gengo_tier, status=STATUS_CREATED)

        lang_buckets = {}
        for job in jobs:
            lang_buckets.setdefault(job.src_lang, []).append(job)

        # For each bucket, assemble an order and post it.
        for lang, jobs in lang_buckets.items():
            batch = []
            for job in jobs:
                batch.append({
                    'id': job.id,
                    'lc_src': job.src_lang,
                    'lc_dst': job.dst_lang,
                    'tier': self.gengo_tier,
                    'text': getattr(job.content_object, job.src_field),
                    'unique_id': job.unique_id
                })

            try:
                resp = gengo_api.translate_bulk(batch)
            except GengoError as exc:
                self.log_error(
                    instance=None, action='push-translations',
                    msg=unicode(exc),
                    metadata={
                        'batch': batch
                    })
                continue

            # We should have an `order_id` at this point, so we create a
            # GengoOrder with it.
            order = GengoOrder(order_id=resp['order_id'])
            order.save()
            order.log('created', metadata={'response': resp})

            # Update all the jobs in the order.
            for job in jobs:
                job.assign_to_order(order)

            if self.gengo_watch_balance:
                # Update the balance and see if we're below the threshold.
                balance = balance - float(resp['credits_used'])

                if not self.balance_good_to_continue(balance, threshold):
                    # If we don't have enough balance, stop.
                    return
Exemple #11
0
    def translate(self, instance, src_lang, src_field, dst_lang, dst_field):
        # If gengosystem is disabled, we just return immediately. We
        # can backfill later.
        if not waffle.switch_is_active('gengosystem'):
            return

        text = getattr(instance, src_field)
        metadata = {
            'tier': self.gengo_tier,
            'locale': instance.locale,
            'length': len(text),
            'body': text[:50].encode('utf-8')
        }

        gengo_api = FjordGengo()

        # Guess the language. If we can't guess the language, then we
        # don't create a GengoJob.
        try:
            lc_src = gengo_api.guess_language(text)
            if lc_src not in gengo_api.get_languages():
                raise GengoUnsupportedLanguage(
                    'unsupported language: {0}'.format(lc_src))

        except GengoUnknownLanguage as exc:
            # FIXME: This might be an indicator that this response is
            # spam. At some point p, we can write code to account for
            # that.
            self.log_error(instance, action='guess-language', msg=unicode(exc),
                           metadata=metadata)
            statsd.incr('translation.{0}.unknown'.format(self.name))
            return

        except GengoUnsupportedLanguage as exc:
            # FIXME: This is a similar boat to GengoUnknownLanguage
            # where for now, we're just going to ignore it because I'm
            # not sure what to do about it and I'd like more data.
            self.log_error(instance, action='translate', msg=unicode(exc),
                           metadata=metadata)
            statsd.incr('translation.{0}.unsupported'.format(self.name))
            return

        # If the locale doesn't equal the guessed language, then
        # that's interesting since the user is writing feedback in a
        # language other than what the ui is showing. We want to log
        # that for metrics purposes.
        if not locale_equals_language(instance.locale, lc_src):
            self.log_error(
                instance,
                action='guess-language',
                msg='locale "{0}" != guessed language "{1}"'.format(
                    instance.locale, lc_src),
                metadata=metadata)

        # If the source language is English, we just copy it over and
        # we're done.
        if locale_equals_language(dst_lang, lc_src):
            setattr(instance, dst_field, text)
            instance.save()
            self.log_info(
                instance, action='translate',
                msg=u'lc_src == dst_lang, so we copy src to dst',
                metadata=metadata)
            return

        if ((self.gengo_check_supported_machine_lc_dst
             and lc_src in GENGO_UNSUPPORTED_MACHINE_LC_SRC)):
            return

        # If src/dst isn't a supported pair, log an issue for metrics
        # purposes and move on.
        if ((self.gengo_check_supported_language_pair
             and (lc_src, dst_lang) not in gengo_api.get_language_pairs())):
            self.log_error(
                instance, action='translate',
                msg=u'(lc_src {0}, dst_lang {1}) not supported'.format(
                    lc_src, dst_lang),
                metadata=metadata)
            return

        job = GengoJob(
            tier=self.gengo_tier,
            content_object=instance,
            src_lang=lc_src,
            src_field=src_field,
            dst_lang=dst_lang,
            dst_field=dst_field
        )
        job.save()
Exemple #12
0
    def push_translations(self):
        # If gengosystem is disabled, we just return immediately. We
        # can backfill later.
        if not waffle.switch_is_active('gengosystem'):
            return

        gengo_api = FjordGengo()

        if not gengo_api.is_configured():
            # If Gengo isn't configured, then we drop out here rather
            # than raise a GengoConfig error.
            return

        if self.gengo_watch_balance:
            balance = gengo_api.get_balance()
            threshold = settings.GENGO_ACCOUNT_BALANCE_THRESHOLD

            # statsd the balance so we can track it with graphite.
            statsd.gauge('translation.gengo.balance', balance)

            if not self.balance_good_to_continue(balance, threshold):
                # If we don't have enough balance, stop.
                return

        # Create language buckets for the jobs for this translator.
        # We bucket by language because this makes it easier for a
        # single Gengo translator to translate all the jobs in an
        # order.
        jobs = GengoJob.objects.filter(tier=self.gengo_tier,
                                       status=STATUS_CREATED)

        lang_buckets = {}
        for job in jobs:
            lang_buckets.setdefault(job.src_lang, []).append(job)

        # For each bucket, assemble an order and post it.
        for lang, jobs in lang_buckets.items():
            batch = []
            for job in jobs:
                batch.append({
                    'id': job.id,
                    'lc_src': job.src_lang,
                    'lc_dst': job.dst_lang,
                    'tier': self.gengo_tier,
                    'text': getattr(job.content_object, job.src_field),
                    'unique_id': job.unique_id
                })

            try:
                resp = gengo_api.translate_bulk(batch)
            except GengoError as exc:
                self.log_error(instance=None,
                               action='push-translations',
                               msg=unicode(exc),
                               metadata={'batch': batch})
                continue

            # We should have an `order_id` at this point, so we create a
            # GengoOrder with it.
            order = GengoOrder(order_id=resp['order_id'])
            order.save()
            order.log('created', metadata={'response': resp})

            # Update all the jobs in the order.
            for job in jobs:
                job.assign_to_order(order)

            if self.gengo_watch_balance:
                # Update the balance and see if we're below the threshold.
                balance = balance - float(resp['credits_used'])

                if not self.balance_good_to_continue(balance, threshold):
                    # If we don't have enough balance, stop.
                    return
Exemple #13
0
    def translate(self, instance, src_lang, src_field, dst_lang, dst_field):
        # If gengosystem is disabled, we just return immediately. We
        # can backfill later.
        if not waffle.switch_is_active('gengosystem'):
            return

        text = getattr(instance, src_field)
        metadata = {
            'tier': self.gengo_tier,
            'locale': instance.locale,
            'length': len(text),
            'body': text[:50].encode('utf-8')
        }

        gengo_api = FjordGengo()

        # Guess the language. If we can't guess the language, then we
        # don't create a GengoJob.
        try:
            lc_src = gengo_api.guess_language(text)
            if lc_src not in gengo_api.get_languages():
                raise GengoUnsupportedLanguage(
                    'unsupported language: {0}'.format(lc_src))

        except GengoUnknownLanguage as exc:
            # FIXME: This might be an indicator that this response is
            # spam. At some point p, we can write code to account for
            # that.
            self.log_error(instance,
                           action='guess-language',
                           msg=unicode(exc),
                           metadata=metadata)
            statsd.incr('translation.{0}.unknown'.format(self.name))
            return

        except GengoUnsupportedLanguage as exc:
            # FIXME: This is a similar boat to GengoUnknownLanguage
            # where for now, we're just going to ignore it because I'm
            # not sure what to do about it and I'd like more data.
            self.log_error(instance,
                           action='translate',
                           msg=unicode(exc),
                           metadata=metadata)
            statsd.incr('translation.{0}.unsupported'.format(self.name))
            return

        # If the locale doesn't equal the guessed language, then
        # that's interesting since the user is writing feedback in a
        # language other than what the ui is showing. We want to log
        # that for metrics purposes.
        if not locale_equals_language(instance.locale, lc_src):
            self.log_error(instance,
                           action='guess-language',
                           msg='locale "{0}" != guessed language "{1}"'.format(
                               instance.locale, lc_src),
                           metadata=metadata)

        # If the source language is English, we just copy it over and
        # we're done.
        if locale_equals_language(dst_lang, lc_src):
            setattr(instance, dst_field, text)
            instance.save()
            self.log_info(instance,
                          action='translate',
                          msg=u'lc_src == dst_lang, so we copy src to dst',
                          metadata=metadata)
            return

        if ((self.gengo_check_supported_machine_lc_dst
             and lc_src in GENGO_UNSUPPORTED_MACHINE_LC_SRC)):
            return

        # If src/dst isn't a supported pair, log an issue for metrics
        # purposes and move on.
        if ((self.gengo_check_supported_language_pair
             and (lc_src, dst_lang) not in gengo_api.get_language_pairs())):
            self.log_error(
                instance,
                action='translate',
                msg=u'(lc_src {0}, dst_lang {1}) not supported'.format(
                    lc_src, dst_lang),
                metadata=metadata)
            return

        job = GengoJob(tier=self.gengo_tier,
                       content_object=instance,
                       src_lang=lc_src,
                       src_field=src_field,
                       dst_lang=dst_lang,
                       dst_field=dst_field)
        job.save()
Exemple #14
0
def gengo_translator_view(request):
    """Covers Gengo-specific translation system status"""
    products = Product.objects.all()
    balance = None
    configured = False
    gengo_languages = None
    missing_prod_locales = None
    outstanding = []
    seven_days_of_orders = []

    if settings.GENGO_PUBLIC_KEY and settings.GENGO_PRIVATE_KEY:
        gengo_api = FjordGengo()
        balance = gengo_api.get_balance()
        configured = True

        # Figure out the list of languages Gengo supports and relate them
        # to PROD locales.
        languages = sorted(gengo_api.get_languages(raw=True)['response'],
                           key=lambda item: item['lc'])
        gengo_languages = []
        PROD_LANG = settings.PROD_LANGUAGES
        for lang in languages:
            lang_lc = lang['lc']
            prod_langs = [
                item for item in PROD_LANG
                if locale_equals_language(item, lang_lc)
            ]
            prod_langs = ' '.join(prod_langs)

            gengo_languages.append((lang_lc, lang['language'], prod_langs))

        # Figure out the list of PROD locales that don't have a Gengo
        # supported language. (Yes, this conflates locale with
        # language which isn't great, but is good enough for now.)
        languages = gengo_api.get_languages()
        missing_prod_locales = []
        for prod_lang in PROD_LANG:
            langs = [
                item for item in languages
                if locale_equals_language(prod_lang, item)
            ]
            if langs:
                continue
            missing_prod_locales.append(prod_lang)

        # How many orders have we created/completed in the last week
        # day-by-day?
        seven_days = datetime.now() - timedelta(days=7)

        orders = GengoOrder.objects.filter(created__gte=seven_days)
        created_by_day = {}
        for order in orders:
            dt = order.created.strftime('%Y-%m-%d')
            created_by_day.setdefault(dt, []).append(order)

        orders = GengoOrder.objects.filter(completed__gte=seven_days)
        completed_by_day = {}
        for order in orders:
            dt = order.completed.strftime('%Y-%m-%d')
            completed_by_day.setdefault(dt, []).append(order)

        # Get date labels in YYYY-mm-dd form for the last 7 days
        days = [(datetime.now() - timedelta(days=i)).strftime('%Y-%m-%d')
                for i in range(7)]

        for day in sorted(days):
            seven_days_of_orders.append((day, len(created_by_day.get(day, [])),
                                         len(completed_by_day.get(day, []))))

        outstanding = [{
            'id': order.id,
            'order_id': order.order_id,
            'created': order.created,
            'total_jobs': order.gengojob_set.all().count(),
            'completed_jobs': order.completed_jobs().count(),
            'outstanding_jobs': order.outstanding_jobs().count(),
        } for order in GengoOrder.objects.filter(completed__isnull=True)]

    return render(
        request, 'admin/gengo_translator_view.html', {
            'title': 'Translations - Gengo Maintenance',
            'configured': configured,
            'settings': settings,
            'products': products,
            'outstanding': outstanding,
            'seven_days_of_orders': seven_days_of_orders,
            'balance': balance,
            'gengo_languages': gengo_languages,
            'missing_prod_locales': missing_prod_locales,
        })