<commit_before>from __future__ import absolute_import
from __future__ import unicode_literals
from calendar import monthrange
from datetime import datetime
from corehq.apps.saved_reports.models import ReportNotification
from corehq.util.soft_assert import soft_assert
from six.moves import range


_soft_assert = soft_assert(
    to='{}<EMAIL>('<EMAIL>', '<EMAIL>'),
    exponential_backoff=False,
)


def _make_all_notification_view_keys(period, as_of, minute):
    if minute == 0:
        # for legacy purposes, on the hour also include reports that didn't have a minute set
        minutes = (None, minute)
    else:
        minutes = (minute,)

    if period == 'daily':
        for minute in minutes:
            yield {
                'startkey': [period, as_of.hour, minute],
                'endkey': [period, as_of.hour, minute, {}],
            }
    elif period == 'weekly':
        for minute in minutes:
            yield {
                'key': [period, as_of.hour, minute, as_of.weekday()],
            }
    else:
        # monthly
        for minute in minutes:
            yield {
                'key': [period, as_of.hour, minute, as_of.day]
            }
        if as_of.day == monthrange(as_of.year, as_of.month)[1]:
            for day in range(as_of.day + 1, 32):
                for minute in minutes:
                    yield {
                        'key': [period, as_of.hour, minute, day]
                    }


def get_scheduled_report_ids(period, as_of=None):
    as_of = as_of or datetime.utcnow()
    assert period in ('daily', 'weekly', 'monthly'), period

    minute = guess_reporting_minute(as_of)

    keys = _make_all_notification_view_keys(period, as_of, minute)
    for key in keys:
        for result in ReportNotification.view(
            "reportconfig/all_notifications",
            reduce=False,
            include_docs=False,
            **key
        ).all():
            yield result['id']


def guess_reporting_minute(now=None):
    """
    Tries to guess a report window based on the current time.
    This is super sketchy - will choose a close time to the hour or 30 minute mark or
    fail hard in the event the task is too far from those.

    Only looks forwards in time.
    """
    now = now or datetime.utcnow()
    window = 5

    for reporting_minute in [0, 15, 30, 45]:
        if reporting_minute <= now.minute <= reporting_minute + window:
            return reporting_minute

    raise ValueError("Couldn't guess reporting minute for time: {}".format(now))<commit_msg>Tighten up interface for _make_all_notification_view_keys<commit_after>from __future__ import absolute_import
from __future__ import unicode_literals
from calendar import monthrange
from datetime import datetime
from corehq.apps.saved_reports.models import ReportNotification
from corehq.util.soft_assert import soft_assert
from six.moves import range


_soft_assert = soft_assert(
    to='{}<EMAIL>('<EMAIL>', '<EMAIL>'),
    exponential_backoff=False,
)


def _make_all_notification_view_keys(period, target):
    """

    :param period: 'daily', 'weekly', or 'monthly'
    :param target: The 15-minute-aligned point in time we are targeting for a match
    :return: generator of couch view kwargs to use in view query for 'reportconfig/all_notifications'
    """
    assert target.minute % 15 == 0
    assert target.second == 0
    assert target.microsecond == 0

    if target.minute == 0:
        # for legacy purposes, on the hour also include reports that didn't have a minute set
        minutes = (None, target.minute)
    else:
        minutes = (target.minute,)

    if period == 'daily':
        for minute in minutes:
            yield {
                'startkey': [period, target.hour, minute],
                'endkey': [period, target.hour, minute, {}],
            }
    elif period == 'weekly':
        for minute in minutes:
            yield {
                'key': [period, target.hour, minute, target.weekday()],
            }
    else:
        # monthly
        for minute in minutes:
            yield {
                'key': [period, target.hour, minute, target.day]
            }
        if target.day == monthrange(target.year, target.month)[1]:
            for day in range(target.day + 1, 32):
                for minute in minutes:
                    yield {
                        'key': [period, target.hour, minute, day]
                    }


def get_scheduled_report_ids(period, as_of=None):
    as_of = as_of or datetime.utcnow()
    assert period in ('daily', 'weekly', 'monthly'), period

    target_minute = guess_reporting_minute(as_of)
    target_point_in_time = as_of.replace(minute=target_minute, second=0, microsecond=0)
    keys = _make_all_notification_view_keys(period, target_point_in_time)
    for key in keys:
        for result in ReportNotification.view(
            "reportconfig/all_notifications",
            reduce=False,
            include_docs=False,
            **key
        ).all():
            yield result['id']


def guess_reporting_minute(now=None):
    """
    Tries to guess a report window based on the current time.
    This is super sketchy - will choose a close time to the hour or 30 minute mark or
    fail hard in the event the task is too far from those.

    Only looks forwards in time.
    """
    now = now or datetime.utcnow()
    window = 5

    for reporting_minute in [0, 15, 30, 45]:
        if reporting_minute <= now.minute <= reporting_minute + window:
            return reporting_minute

    raise ValueError("Couldn't guess reporting minute for time: {}".format(now))
{'fraction_non_alphanumeric': 0.09330954504906333, 'fraction_numerical': 0.00695807314897413, 'mean_word_length': 2.3710162357185807, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3732229', 'n_tokens_mistral': 1645, 'n_tokens_neox': 1558, 'n_words': 511}