<commit_before>from collections import namedtuple
from urllib import urlencode

from django.template.loader import render_to_string
from django.utils.safestring import mark_safe, mark_for_escaping
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _, get_language
from django.utils.translation import ugettext_noop, ugettext_lazy
from django.core.cache import cache

from corehq import toggles, privileges, Domain
from corehq.apps.accounting.dispatcher import AccountingAdminInterfaceDispatcher
from corehq.apps.accounting.models import BillingAccountAdmin, Invoice
from corehq.apps.accounting.utils import is_accounting_admin
from corehq.apps.domain.utils import get_adm_enabled_domains
from corehq.apps.hqadmin.reports import (
    RealProjectSpacesReport,
    CommConnectProjectSpacesReport,
    CommTrackProjectSpacesReport,
)
from corehq.apps.hqwebapp.utils import (
    format_second_level_context,
    format_submenu_context,
    sidebar_to_dropdown
)
from corehq.apps.indicators.dispatcher import IndicatorAdminInterfaceDispatcher
from corehq.apps.indicators.utils import get_indicator_domains
from corehq.apps.reminders.util import can_use_survey_reminders
from corehq.apps.smsbillables.dispatcher import SMSAdminInterfaceDispatcher
from django_prbac.utils import has_privilege

from dimagi.utils.couch.database import get_db
from dimagi.utils.decorators.memoized import memoized
from dimagi.utils.django.cache import make_template_fragment_key

from corehq.apps.reports.dispatcher import (ProjectReportDispatcher,
                                            CustomProjectReportDispatcher)
from corehq.apps.reports.models import ReportConfig
from corehq.apps.adm.dispatcher import (ADMAdminInterfaceDispatcher,
                                        ADMSectionDispatcher)
from corehq.apps.announcements.dispatcher import (
    HQAnnouncementAdminInterfaceDispatcher)
from corehq.toggles import IS_DEVELOPER


class GaTracker(namedtuple('GaTracking', 'category action label')):
    """
    Info for tracking clicks using Google Analytics
    see https://developers.google.com/analytics/devguides/collection/analyticsjs/events
    """
    def __new__(cls, category, action, label=None):
        return super(GaTracker, cls).__new__(cls, category, action, label)


class UITab(object):
    title = None
    view = None
    subtab_classes = None

    dispatcher = None

    # must be instance of GaTracker
    ga_tracker = None

    def __init__(self, request, current_url_name, domain=None, couch_user=None,
                 project=None, org=None):
        if self.subtab_classes:
            self.subtabs = [cls(request, current_url_name, domain=domain,
                                couch_user=couch_user, project=project,
                                org=org)
                            for cls in self.subtab_classes]
        else:
            self.subtabs = None

        self.domain = domain
        self.couch_user = couch_user
        self.project = project
        self.org = org

        # This should not be considered as part of the subclass API unless it
        # is necessary. Try to add new explicit parameters instead.
        self._request = request
        self._current_url_name = current_url_name

    @property
    def dropdown_items(self):
        # todo: add default implementation which looks at sidebar_items and
        # sees which ones have is_dropdown_visible or something like that.
        # Also make it work for tabs with subtabs.
        return sidebar_to_dropdown(sidebar_items=self.sidebar_items,
                                   domain=self.domain, current_url_name=self.url)

    @property
    @memoized
    def sidebar_items(self):
        if self.dispatcher:
            context = {
                'request': self._request,
                'domain': self.domain,
            }
            return self.dispatcher.navigation_sections(context)
        else:
            return []

    @property
    def is_viewable(self):
        """
        Whether the tab should be displayed.  Subclass implementations can skip
        checking whether domain, couch_user, or project is not None before
        accessing an attribute of them -- this property is accessed in
        real_is_viewable and wrapped in a try block that returns False in the
        case of an AttributeError for any of those variables.

        """
        raise NotImplementedError()

    @property
    @memoized
    def real_is_viewable(self):
        if self.subtabs:
            return any(st.real_is_viewable for st in self.subtabs)
        else:
            try:
                return self.is_viewable
            except AttributeError:
                return False

    @property
    @memoized
    def url(self):
        try:
            if self.domain:
                return reverse(self.view, args=[self.domain])
            if self.org:
                return reverse(self.view, args=[self.org.name])
        except Exception:
            pass

        try:
            return reverse(self.view)
        except Exception:
            return None

    @property
    def is_active_shortcircuit(self):
        return None

    @property
    def is_active_fast(self):
        shortcircuit = self.is_active_shortcircuit
        if shortcircuit is not None:
            return shortcircuit

        request_path = self._request.get_full_path()
        return self.url and request_path.startswith(self.url)

    @property
    @memoized
    def is_active(self):
        shortcircuit = self.is_active_shortcircuit
        if shortcircuit is not None:
            return shortcircuit

        request_path = self._request.get_full_path()

        if self.urls:
            if (any(request_path.startswith(url) for url in self.urls) or
                    self._current_url_name in self.subpage_url_names):
                return True
        elif self.subtabs and any(st.is_active for st in self.subtabs):
            return True

    @property
    @memoized
    def urls(self):
        urls = [self.url] if self.url else []
        if self.subtabs:
            for st in self.subtabs:
                urls.extend(st.urls)

        try:
            for name, section in self.sidebar_items:
                urls.extend(item['url'] for item in section)
        except Exception:
            # tried to get urls for another tab on a page that doesn't provide
            # the necessary couch_user, domain, project, etc. value
            pass

        return urls

    @property
    @memoized
    def subpage_url_names(self):
        """
        List of all url names of subpages of sidebar items that get
        displayed only when you're on that subpage.
        """
        names = []
        if self.subtabs:
            for st in self.subtabs:
                names.extend(st.subpage_url_names)

        try:
            for name, section in self.sidebar_items:
                names.extend(subpage['urlname']
                             for item in section
                             for subpage in item.get('subpages', []))
        except Exception:
            pass

        return names

    @classmethod
    def clear_dropdown_cache(cls, request, domain):
        for is_active in True, False:
            if hasattr(cls, 'get_view'):
                view = cls.get_view(domain)
            else:
                view = cls.view
            key = make_template_fragment_key('header_tab', [
                domain,
                None,  # tab.org should be None for any non org page
                view,
                is_active,
                request.couch_user.get_id,
                get_language(),
            ])
            cache.delete(key)


    @property
    def css_id(self):
        return self.__class__.__name__


class ProjectReportsTab(UITab):
    title = ugettext_noop("Project Reports")
    view = "corehq.apps.reports.views.default"

    @property
    def is_active_shortcircuit(self):
        # HACK. We need a more overarching way to avoid doing things this way
        if 'reports/adm' in self._request.get_full_path():
            return False

    @property
    def is_viewable(self):
        return (self.domain and self.project and
                not self.project.is_snapshot and
                (self.couch_user.can_view_reports() or
                 self.couch_user.get_viewable_reports()))

    @property
    def sidebar_items(self):
        context = {
            'request': self._request,
            'domain': self.domain,
        }

        tools = [(_("Tools"), [
            {'title': _('My Saved Reports'),
             'url': reverse('saved_reports', args=[self.domain]),
             'icon': 'icon-tasks',
             'show_in_dropdown': True}
        ])]

        project_reports = ProjectReportDispatcher.navigation_sections(context)
        custom_reports = CustomProjectReportDispatcher.navigation_sections(
            context)

        return tools + project_reports + custom_reports


class ADMReportsTab(UITab):
    title = ugettext_noop("Active Data Management")
    view = "corehq.apps.adm.views.default_adm_report"
    dispatcher = ADMSectionDispatcher

    @property
    def is_viewable(self):
        if not self.project or self.project.commtrack_enabled:
            return False

        adm_enabled_projects = get_adm_enabled_domains()

        return (not self.project.is_snapshot and
                self.domain in adm_enabled_projects and
                (self.couch_user.can_view_reports() or
                 self.couch_user.get_viewable_reports()))


class IndicatorAdminTab(UITab):
    title = ugettext_noop("Administer Indicators")
    view = "corehq.apps.indicators.views.default_admin"
    dispatcher = IndicatorAdminInterfaceDispatcher

    @property
    def is_viewable(self):
        indicator_enabled_projects = get_indicator_domains()
        return (self.couch_user.can_edit_data() and
                self.domain in indicator_enabled_projects)

    @property
    def sidebar_items(self):
        items = super(IndicatorAdminTab, self).sidebar_items
        from corehq.apps.indicators.views import (
            BulkExportIndicatorsView,
            BulkImportIndicatorsView,
        )
        items.append([
            _("Other Actions"), [
                {
                    'title': BulkImportIndicatorsView.page_title,
                    'url': reverse(BulkImportIndicatorsView.urlname,
                                   args=[self.domain]),
                    'urlname': BulkImportIndicatorsView.urlname,
                },
                {
                    'title': _("Download Indicators Export"),
                    'url': reverse(BulkExportIndicatorsView.urlname,
                                   args=[self.domain]),
                }
            ]
        ])
        return items


class DashboardTab(UITab):
    title = ugettext_noop("Dashboard")
    view = 'corehq.apps.dashboard.views.dashboard_default'

    @property
    def is_viewable(self):
        return (self.domain and self.project and not self.project.is_snapshot
                and self.couch_user)


class ReportsTab(UITab):
    title = ugettext_noop("Reports")
    subtab_classes = (ProjectReportsTab, ADMReportsTab, IndicatorAdminTab)

    @property
    def view(self):
        return self.get_view(self.domain)

    @staticmethod
    def get_view(domain):
        module = Domain.get_module_by_name(domain)
        if hasattr(module, 'DEFAULT_REPORT_CLASS'):
            return "corehq.apps.reports.views.default"
        return "corehq.apps.reports.views.saved_reports"

    @property
    def dropdown_items(self):
        saved_report_header = format_submenu_context(_('My Saved Reports'),
                                                     is_header=True)
        saved_report_list = [
            format_submenu_context(config.name, url=config.url)
            for config in ReportConfig.by_domain_and_owner(self.domain,
                                                           self.couch_user._id)
        ]
        if saved_report_list:
            saved_reports_dropdown = ([saved_report_header] + saved_report_list)
        else:
            saved_reports_dropdown = []

        context = {
            'request': self._request,
            'domain': self.domain,
        }
        reports = sidebar_to_dropdown(
            ProjectReportDispatcher.navigation_sections(context),<commit_msg>render saved-report dropdowns into second level - if too many<commit_after>from collections import namedtuple
from urllib import urlencode

from django.template.loader import render_to_string
from django.utils.safestring import mark_safe, mark_for_escaping
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _, get_language
from django.utils.translation import ugettext_noop, ugettext_lazy
from django.core.cache import cache

from corehq import toggles, privileges, Domain
from corehq.apps.accounting.dispatcher import AccountingAdminInterfaceDispatcher
from corehq.apps.accounting.models import BillingAccountAdmin, Invoice
from corehq.apps.accounting.utils import is_accounting_admin
from corehq.apps.domain.utils import get_adm_enabled_domains
from corehq.apps.hqadmin.reports import (
    RealProjectSpacesReport,
    CommConnectProjectSpacesReport,
    CommTrackProjectSpacesReport,
)
from corehq.apps.hqwebapp.utils import (
    format_second_level_context,
    format_submenu_context,
    sidebar_to_dropdown
)
from corehq.apps.indicators.dispatcher import IndicatorAdminInterfaceDispatcher
from corehq.apps.indicators.utils import get_indicator_domains
from corehq.apps.reminders.util import can_use_survey_reminders
from corehq.apps.smsbillables.dispatcher import SMSAdminInterfaceDispatcher
from django_prbac.utils import has_privilege

from dimagi.utils.couch.database import get_db
from dimagi.utils.decorators.memoized import memoized
from dimagi.utils.django.cache import make_template_fragment_key

from corehq.apps.reports.dispatcher import (ProjectReportDispatcher,
                                            CustomProjectReportDispatcher)
from corehq.apps.reports.models import ReportConfig
from corehq.apps.adm.dispatcher import (ADMAdminInterfaceDispatcher,
                                        ADMSectionDispatcher)
from corehq.apps.announcements.dispatcher import (
    HQAnnouncementAdminInterfaceDispatcher)
from corehq.toggles import IS_DEVELOPER


class GaTracker(namedtuple('GaTracking', 'category action label')):
    """
    Info for tracking clicks using Google Analytics
    see https://developers.google.com/analytics/devguides/collection/analyticsjs/events
    """
    def __new__(cls, category, action, label=None):
        return super(GaTracker, cls).__new__(cls, category, action, label)


class UITab(object):
    title = None
    view = None
    subtab_classes = None

    dispatcher = None

    # must be instance of GaTracker
    ga_tracker = None

    def __init__(self, request, current_url_name, domain=None, couch_user=None,
                 project=None, org=None):
        if self.subtab_classes:
            self.subtabs = [cls(request, current_url_name, domain=domain,
                                couch_user=couch_user, project=project,
                                org=org)
                            for cls in self.subtab_classes]
        else:
            self.subtabs = None

        self.domain = domain
        self.couch_user = couch_user
        self.project = project
        self.org = org

        # This should not be considered as part of the subclass API unless it
        # is necessary. Try to add new explicit parameters instead.
        self._request = request
        self._current_url_name = current_url_name

    @property
    def dropdown_items(self):
        # todo: add default implementation which looks at sidebar_items and
        # sees which ones have is_dropdown_visible or something like that.
        # Also make it work for tabs with subtabs.
        return sidebar_to_dropdown(sidebar_items=self.sidebar_items,
                                   domain=self.domain, current_url_name=self.url)

    @property
    @memoized
    def sidebar_items(self):
        if self.dispatcher:
            context = {
                'request': self._request,
                'domain': self.domain,
            }
            return self.dispatcher.navigation_sections(context)
        else:
            return []

    @property
    def is_viewable(self):
        """
        Whether the tab should be displayed.  Subclass implementations can skip
        checking whether domain, couch_user, or project is not None before
        accessing an attribute of them -- this property is accessed in
        real_is_viewable and wrapped in a try block that returns False in the
        case of an AttributeError for any of those variables.

        """
        raise NotImplementedError()

    @property
    @memoized
    def real_is_viewable(self):
        if self.subtabs:
            return any(st.real_is_viewable for st in self.subtabs)
        else:
            try:
                return self.is_viewable
            except AttributeError:
                return False

    @property
    @memoized
    def url(self):
        try:
            if self.domain:
                return reverse(self.view, args=[self.domain])
            if self.org:
                return reverse(self.view, args=[self.org.name])
        except Exception:
            pass

        try:
            return reverse(self.view)
        except Exception:
            return None

    @property
    def is_active_shortcircuit(self):
        return None

    @property
    def is_active_fast(self):
        shortcircuit = self.is_active_shortcircuit
        if shortcircuit is not None:
            return shortcircuit

        request_path = self._request.get_full_path()
        return self.url and request_path.startswith(self.url)

    @property
    @memoized
    def is_active(self):
        shortcircuit = self.is_active_shortcircuit
        if shortcircuit is not None:
            return shortcircuit

        request_path = self._request.get_full_path()

        if self.urls:
            if (any(request_path.startswith(url) for url in self.urls) or
                    self._current_url_name in self.subpage_url_names):
                return True
        elif self.subtabs and any(st.is_active for st in self.subtabs):
            return True

    @property
    @memoized
    def urls(self):
        urls = [self.url] if self.url else []
        if self.subtabs:
            for st in self.subtabs:
                urls.extend(st.urls)

        try:
            for name, section in self.sidebar_items:
                urls.extend(item['url'] for item in section)
        except Exception:
            # tried to get urls for another tab on a page that doesn't provide
            # the necessary couch_user, domain, project, etc. value
            pass

        return urls

    @property
    @memoized
    def subpage_url_names(self):
        """
        List of all url names of subpages of sidebar items that get
        displayed only when you're on that subpage.
        """
        names = []
        if self.subtabs:
            for st in self.subtabs:
                names.extend(st.subpage_url_names)

        try:
            for name, section in self.sidebar_items:
                names.extend(subpage['urlname']
                             for item in section
                             for subpage in item.get('subpages', []))
        except Exception:
            pass

        return names

    @classmethod
    def clear_dropdown_cache(cls, request, domain):
        for is_active in True, False:
            if hasattr(cls, 'get_view'):
                view = cls.get_view(domain)
            else:
                view = cls.view
            key = make_template_fragment_key('header_tab', [
                domain,
                None,  # tab.org should be None for any non org page
                view,
                is_active,
                request.couch_user.get_id,
                get_language(),
            ])
            cache.delete(key)


    @property
    def css_id(self):
        return self.__class__.__name__


class ProjectReportsTab(UITab):
    title = ugettext_noop("Project Reports")
    view = "corehq.apps.reports.views.default"

    @property
    def is_active_shortcircuit(self):
        # HACK. We need a more overarching way to avoid doing things this way
        if 'reports/adm' in self._request.get_full_path():
            return False

    @property
    def is_viewable(self):
        return (self.domain and self.project and
                not self.project.is_snapshot and
                (self.couch_user.can_view_reports() or
                 self.couch_user.get_viewable_reports()))

    @property
    def sidebar_items(self):
        context = {
            'request': self._request,
            'domain': self.domain,
        }

        tools = [(_("Tools"), [
            {'title': _('My Saved Reports'),
             'url': reverse('saved_reports', args=[self.domain]),
             'icon': 'icon-tasks',
             'show_in_dropdown': True}
        ])]

        project_reports = ProjectReportDispatcher.navigation_sections(context)
        custom_reports = CustomProjectReportDispatcher.navigation_sections(
            context)

        return tools + project_reports + custom_reports


class ADMReportsTab(UITab):
    title = ugettext_noop("Active Data Management")
    view = "corehq.apps.adm.views.default_adm_report"
    dispatcher = ADMSectionDispatcher

    @property
    def is_viewable(self):
        if not self.project or self.project.commtrack_enabled:
            return False

        adm_enabled_projects = get_adm_enabled_domains()

        return (not self.project.is_snapshot and
                self.domain in adm_enabled_projects and
                (self.couch_user.can_view_reports() or
                 self.couch_user.get_viewable_reports()))


class IndicatorAdminTab(UITab):
    title = ugettext_noop("Administer Indicators")
    view = "corehq.apps.indicators.views.default_admin"
    dispatcher = IndicatorAdminInterfaceDispatcher

    @property
    def is_viewable(self):
        indicator_enabled_projects = get_indicator_domains()
        return (self.couch_user.can_edit_data() and
                self.domain in indicator_enabled_projects)

    @property
    def sidebar_items(self):
        items = super(IndicatorAdminTab, self).sidebar_items
        from corehq.apps.indicators.views import (
            BulkExportIndicatorsView,
            BulkImportIndicatorsView,
        )
        items.append([
            _("Other Actions"), [
                {
                    'title': BulkImportIndicatorsView.page_title,
                    'url': reverse(BulkImportIndicatorsView.urlname,
                                   args=[self.domain]),
                    'urlname': BulkImportIndicatorsView.urlname,
                },
                {
                    'title': _("Download Indicators Export"),
                    'url': reverse(BulkExportIndicatorsView.urlname,
                                   args=[self.domain]),
                }
            ]
        ])
        return items


class DashboardTab(UITab):
    title = ugettext_noop("Dashboard")
    view = 'corehq.apps.dashboard.views.dashboard_default'

    @property
    def is_viewable(self):
        return (self.domain and self.project and not self.project.is_snapshot
                and self.couch_user)


class ReportsTab(UITab):
    title = ugettext_noop("Reports")
    subtab_classes = (ProjectReportsTab, ADMReportsTab, IndicatorAdminTab)

    @property
    def view(self):
        return self.get_view(self.domain)

    @staticmethod
    def get_view(domain):
        module = Domain.get_module_by_name(domain)
        if hasattr(module, 'DEFAULT_REPORT_CLASS'):
            return "corehq.apps.reports.views.default"
        return "corehq.apps.reports.views.saved_reports"

    @property
    def dropdown_items(self):
        saved_report_header = format_submenu_context(_('My Saved Reports'),
                                                     is_header=True)
        saved_reports_list = list(ReportConfig.by_domain_and_owner(
                                  self.domain,
                                  self.couch_user._id))

        MAX_DISPLAYABLE_SAVED_REPORTS = 5
        first_five_items = [
            format_submenu_context(saved_report.name, url=saved_report.url)
            for counter, saved_report in enumerate(saved_reports_list)
            if counter < MAX_DISPLAYABLE_SAVED_REPORTS
        ]
        rest_as_second_level_items = [
            format_second_level_context("...", "#", [
                format_submenu_context(saved_report.name, url=saved_report.url)
                for counter, saved_report in enumerate(saved_reports_list)
                if counter >= MAX_DISPLAYABLE_SAVED_REPORTS
            ])
        ] if len(saved_reports_list) > MAX_DISPLAYABLE_SAVED_REPORTS else []

        if first_five_items:
            saved_reports_dropdown = ([saved_report_header] + first_five_items + rest_as_second_level_items)
        else:
            saved_reports_dropdown = []

        context = {
            'request': self._request,
            'domain': self.domain,
        }
        reports = sidebar_to_dropdown(
            ProjectReportDispatcher.navigation_sections(context),
{'fraction_non_alphanumeric': 0.07140343330856763, 'fraction_numerical': 3.910374222813123e-05, 'mean_word_length': 2.1437000614628148, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1053144', 'n_tokens_mistral': 7165, 'n_tokens_neox': 6819, 'n_words': 1653}