<commit_before>from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from django.template.loader import render_to_string
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy

from memoized import memoized

from corehq import privileges
from corehq.apps.accounting.models import BillingAccount
from corehq.apps.accounting.utils import domain_has_privilege
from corehq.apps.reports.datatables import DataTablesColumn, DataTablesHeader
from corehq.apps.reports.dispatcher import UserManagementReportDispatcher
from corehq.apps.reports.filters.users import (
    ChangeActionFilter,
    ChangedByUserFilter,
    EnterpriseUserFilter,
)
from corehq.apps.reports.filters.users import \
    ExpandedMobileWorkerFilter as EMWF
from corehq.apps.reports.generic import GenericTabularReport, GetParamsMixin, PaginatedReportMixin
from corehq.apps.reports.standard import DatespanMixin, ProjectReport
from corehq.apps.users.audit.change_messages import (
    ASSIGNED_LOCATIONS_FIELD,
    CHANGE_MESSAGES_FIELDS,
    DOMAIN_FIELD,
    LOCATION_FIELD,
    PHONE_NUMBERS_FIELD,
    ROLE_FIELD,
    TWO_FACTOR_FIELD,
    get_messages,
)
from corehq.apps.users.models import UserHistory
from corehq.const import USER_DATETIME_FORMAT
from corehq.util.timezones.conversions import ServerTime

class UserHistoryReport(GetParamsMixin, DatespanMixin, GenericTabularReport, ProjectReport, PaginatedReportMixin):
    slug = 'user_history'
    name = ugettext_lazy("User History")
    section_name = ugettext_lazy("User Management")

    exportable = True
    exportable_all = True

    dispatcher = UserManagementReportDispatcher

    fields = [
        'corehq.apps.reports.filters.users.AffectedUserFilter',
        'corehq.apps.reports.filters.users.ChangedByUserFilter',
        'corehq.apps.reports.filters.dates.DatespanFilter',
        'corehq.apps.reports.filters.users.ChangeActionFilter',
        'corehq.apps.reports.filters.users.UserPropertyFilter',
        'corehq.apps.reports.filters.users.UserUploadRecordFilter',
    ]

    description = ugettext_lazy("History of user updates")
    ajax_pagination = True
    default_sort = {'changed_at': 'desc'}

    @classmethod
    def get_primary_properties(cls, domain):
        """
        Get slugs and human-friendly names for the properties that are available
        for filtering and/or displayed by default in the report, without
        needing to click "See More".
        """
        if domain_has_privilege(domain, privileges.APP_USER_PROFILES):
            user_data_label = _("profile or user data")
        else:
            user_data_label = _("user data")
        return {
            "username": _("username"),
            ROLE_FIELD: _("role"),
            "email": _("email"),
            DOMAIN_FIELD: _("project"),
            "is_active": _("is active"),
            "language": _("language"),
            PHONE_NUMBERS_FIELD: _("phone numbers"),
            LOCATION_FIELD: _("primary location"),
            "user_data": user_data_label,
            TWO_FACTOR_FIELD: _("two factor authentication disabled"),
            ASSIGNED_LOCATIONS_FIELD: _("assigned locations"),
        }

    @property
    def headers(self):
        h = [
            DataTablesColumn(_("Affected User"), sortable=False),
            DataTablesColumn(_("Modified by User"), sortable=False),
            DataTablesColumn(_("Action"), prop_name='action'),
            DataTablesColumn(_("Via"), prop_name='changed_via'),
            DataTablesColumn(_("Changes"), sortable=False),
            DataTablesColumn(_("Change Message"), sortable=False),
            DataTablesColumn(_("Timestamp"), prop_name='changed_at'),
        ]

        return DataTablesHeader(*h)

    @property
    def total_records(self):
        return self._get_queryset().count()

    @memoized
    def _get_queryset(self):
        user_slugs = self.request.GET.getlist(EMWF.slug)
        user_ids = self._get_user_ids(user_slugs)
        # return empty queryset if no matching users were found
        if user_slugs and not user_ids:
            return UserHistory.objects.none()

        changed_by_user_slugs = self.request.GET.getlist(ChangedByUserFilter.slug)
        changed_by_user_ids = self._get_user_ids(changed_by_user_slugs)
        # return empty queryset if no matching users were found
        if changed_by_user_slugs and not changed_by_user_ids:
            return UserHistory.objects.none()

        user_property = self.request.GET.get('user_property')
        actions = self.request.GET.getlist('action')
        user_upload_record_id = self.request.GET.get('user_upload_record')
        query = self._build_query(user_ids, changed_by_user_ids, user_property, actions, user_upload_record_id)
        return query

    def _get_user_ids(self, slugs):
        es_query = self._get_users_es_query(slugs)
        return es_query.values_list('_id', flat=True)

    def _get_users_es_query(self, slugs):
        return EnterpriseUserFilter.user_es_query(
            self.domain,
            slugs,
            self.request.couch_user,
        )

    def _build_query(self, user_ids, changed_by_user_ids, user_property, actions, user_upload_record_id):
        filters = Q(for_domain__in=self._for_domains())

        if user_ids:
            filters = filters & Q(user_id__in=user_ids)

        if changed_by_user_ids:
            filters = filters & Q(changed_by__in=changed_by_user_ids)

        if user_property:
            filters = filters & self._get_property_filters(user_property)

        if actions and ChangeActionFilter.ALL not in actions:
            filters = filters & Q(action__in=actions)

        if user_upload_record_id:
            filters = filters & Q(user_upload_record_id=user_upload_record_id)

        if self.datespan:
            filters = filters & Q(changed_at__lt=self.datespan.enddate_adjusted,
                                  changed_at__gte=self.datespan.startdate)
        return UserHistory.objects.filter(filters)

    def _for_domains(self):
        return BillingAccount.get_account_by_domain(self.domain).get_domains()

    @staticmethod
    def _get_property_filters(user_property):
        if user_property in CHANGE_MESSAGES_FIELDS:
            query_filters = Q(change_messages__has_key=user_property)
            # to include CommCareUser creation from UI where a location can be assigned as a part of user creation
            # which is tracked only under "changes" and not "change messages"
            if user_property == LOCATION_FIELD:
                query_filters = query_filters | Q(changes__has_key='location_id')
        else:
            query_filters = Q(changes__has_key=user_property)
        return query_filters

    def get_rows(self, for_export):
        records = self._get_queryset().order_by(self.ordering)[
            self.pagination.start:self.pagination.start + self.pagination.count
        ]
        for record in records:
            yield self._user_history_row(record, self.domain, self.timezone, for_export)

    @property
    def rows(self):
        return self.get_rows(for_export=False)

    # Override parent method to add new lines to cell values of certain columns
    @property
    def export_rows(self):
        return self.get_rows(for_export=True)

    @property
    def ordering(self):
        by, direction = list(self.get_sorting_block()[0].items())[0]
        return '-' + by if direction == 'desc' else by

    @memoized
    def _get_location_name(self, location_id):
        from corehq.apps.locations.models import SQLLocation
        if not location_id:
            return None
        try:
            location_object = SQLLocation.objects.get(location_id=location_id)
        except ObjectDoesNotExist:
            return None
        return location_object.display_name

    def _user_history_row(self, record, domain, timezone, for_export):
        if for_export:
            change_messages = ", \n".join(list(get_messages(record.change_messages)))
        else:
            change_messages = self._html_list(list(get_messages(record.change_messages)))
        return [
            record.user_repr,
            record.changed_by_repr,
            _get_action_display(record.action),
            record.changed_via,
            self._user_history_details_cell(record.changes, domain, for_export),
            change_messages,
            ServerTime(record.changed_at).user_time(timezone).ui_string(USER_DATETIME_FORMAT),
        ]

    def _html_list(self, changes):
        items = []
        if isinstance(changes, dict):
            for key, value in changes.items():
                if isinstance(value, dict):
                    value = self._html_list(value)
                elif isinstance(value, list):
                    value = format_html(", ".join(value))
                else:
                    value = format_html(str(value))
                items.append("
{}: {}
".format(key, value))
        elif isinstance(changes, list):
            items = ["
{}
".format(format_html(change)) for change in changes]
        return mark_safe(f"
{''.join(items)}
")

    def _user_history_details_cell(self, changes, domain, for_export):
        properties = UserHistoryReport.get_primary_properties(domain)
        properties.pop("user_data", None)
        primary_changes = {}
        all_changes = {}

        for key, value in changes.items():
            if key == 'location_id':
                value = self._get_location_name(value)
                primary_changes[properties[LOCATION_FIELD]] = value
                all_changes[properties[LOCATION_FIELD]] = value
            elif key == 'user_data':
                for user_data_key, user_data_value in changes['user_data'].items():
                    all_changes[f"user data: {user_data_key}"] = user_data_value
            elif key in properties:
                primary_changes[properties[key]] = value
                all_changes[properties[key]] = value
        more_count = len(all_changes) - len(primary_changes)

        if for_export:
            # This just adds a comma and newline between each change
            return ", \n".join(
                [f"{key}: {value or _('None')}" for key, value in list(all_changes.items())]
            )
        else:
            return render_to_string("reports/standard/partials/user_history_changes.html", {
                "primary_changes": self._html_list(primary_changes),
                "all_changes": self._html_list(all_changes),
                "more_count": more_count,
            })

def _get_action_display(logged_action):
    action = ugettext_lazy("Updated")
    if logged_action == UserHistory.CREATE:
        action = ugettext_lazy("Added")
    elif logged_action == UserHistory.DELETE:
        action = ugettext_lazy("Deleted")
    return action
<commit_msg>add all rows to export instead of just first page<commit_after>from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from django.template.loader import render_to_string
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy

from memoized import memoized

from corehq import privileges
from corehq.apps.accounting.models import BillingAccount
from corehq.apps.accounting.utils import domain_has_privilege
from corehq.apps.reports.datatables import DataTablesColumn, DataTablesHeader
from corehq.apps.reports.dispatcher import UserManagementReportDispatcher
from corehq.apps.reports.filters.users import (
    ChangeActionFilter,
    ChangedByUserFilter,
    EnterpriseUserFilter,
)
from corehq.apps.reports.filters.users import \
    ExpandedMobileWorkerFilter as EMWF
from corehq.apps.reports.generic import GenericTabularReport, GetParamsMixin, PaginatedReportMixin
from corehq.apps.reports.standard import DatespanMixin, ProjectReport
from corehq.apps.users.audit.change_messages import (
    ASSIGNED_LOCATIONS_FIELD,
    CHANGE_MESSAGES_FIELDS,
    DOMAIN_FIELD,
    LOCATION_FIELD,
    PHONE_NUMBERS_FIELD,
    ROLE_FIELD,
    TWO_FACTOR_FIELD,
    get_messages,
)
from corehq.apps.users.models import UserHistory
from corehq.const import USER_DATETIME_FORMAT
from corehq.util.timezones.conversions import ServerTime

class UserHistoryReport(GetParamsMixin, DatespanMixin, GenericTabularReport, ProjectReport, PaginatedReportMixin):
    slug = 'user_history'
    name = ugettext_lazy("User History")
    section_name = ugettext_lazy("User Management")

    exportable = True
    exportable_all = True

    dispatcher = UserManagementReportDispatcher

    fields = [
        'corehq.apps.reports.filters.users.AffectedUserFilter',
        'corehq.apps.reports.filters.users.ChangedByUserFilter',
        'corehq.apps.reports.filters.dates.DatespanFilter',
        'corehq.apps.reports.filters.users.ChangeActionFilter',
        'corehq.apps.reports.filters.users.UserPropertyFilter',
        'corehq.apps.reports.filters.users.UserUploadRecordFilter',
    ]

    description = ugettext_lazy("History of user updates")
    ajax_pagination = True
    default_sort = {'changed_at': 'desc'}

    @classmethod
    def get_primary_properties(cls, domain):
        """
        Get slugs and human-friendly names for the properties that are available
        for filtering and/or displayed by default in the report, without
        needing to click "See More".
        """
        if domain_has_privilege(domain, privileges.APP_USER_PROFILES):
            user_data_label = _("profile or user data")
        else:
            user_data_label = _("user data")
        return {
            "username": _("username"),
            ROLE_FIELD: _("role"),
            "email": _("email"),
            DOMAIN_FIELD: _("project"),
            "is_active": _("is active"),
            "language": _("language"),
            PHONE_NUMBERS_FIELD: _("phone numbers"),
            LOCATION_FIELD: _("primary location"),
            "user_data": user_data_label,
            TWO_FACTOR_FIELD: _("two factor authentication disabled"),
            ASSIGNED_LOCATIONS_FIELD: _("assigned locations"),
        }

    @property
    def headers(self):
        h = [
            DataTablesColumn(_("Affected User"), sortable=False),
            DataTablesColumn(_("Modified by User"), sortable=False),
            DataTablesColumn(_("Action"), prop_name='action'),
            DataTablesColumn(_("Via"), prop_name='changed_via'),
            DataTablesColumn(_("Changes"), sortable=False),
            DataTablesColumn(_("Change Message"), sortable=False),
            DataTablesColumn(_("Timestamp"), prop_name='changed_at'),
        ]

        return DataTablesHeader(*h)

    @property
    def total_records(self):
        return self._get_queryset().count()

    @memoized
    def _get_queryset(self):
        user_slugs = self.request.GET.getlist(EMWF.slug)
        user_ids = self._get_user_ids(user_slugs)
        # return empty queryset if no matching users were found
        if user_slugs and not user_ids:
            return UserHistory.objects.none()

        changed_by_user_slugs = self.request.GET.getlist(ChangedByUserFilter.slug)
        changed_by_user_ids = self._get_user_ids(changed_by_user_slugs)
        # return empty queryset if no matching users were found
        if changed_by_user_slugs and not changed_by_user_ids:
            return UserHistory.objects.none()

        user_property = self.request.GET.get('user_property')
        actions = self.request.GET.getlist('action')
        user_upload_record_id = self.request.GET.get('user_upload_record')
        query = self._build_query(user_ids, changed_by_user_ids, user_property, actions, user_upload_record_id)
        return query

    def _get_user_ids(self, slugs):
        es_query = self._get_users_es_query(slugs)
        return es_query.values_list('_id', flat=True)

    def _get_users_es_query(self, slugs):
        return EnterpriseUserFilter.user_es_query(
            self.domain,
            slugs,
            self.request.couch_user,
        )

    def _build_query(self, user_ids, changed_by_user_ids, user_property, actions, user_upload_record_id):
        filters = Q(for_domain__in=self._for_domains())

        if user_ids:
            filters = filters & Q(user_id__in=user_ids)

        if changed_by_user_ids:
            filters = filters & Q(changed_by__in=changed_by_user_ids)

        if user_property:
            filters = filters & self._get_property_filters(user_property)

        if actions and ChangeActionFilter.ALL not in actions:
            filters = filters & Q(action__in=actions)

        if user_upload_record_id:
            filters = filters & Q(user_upload_record_id=user_upload_record_id)

        if self.datespan:
            filters = filters & Q(changed_at__lt=self.datespan.enddate_adjusted,
                                  changed_at__gte=self.datespan.startdate)
        return UserHistory.objects.filter(filters)

    def _for_domains(self):
        return BillingAccount.get_account_by_domain(self.domain).get_domains()

    @staticmethod
    def _get_property_filters(user_property):
        if user_property in CHANGE_MESSAGES_FIELDS:
            query_filters = Q(change_messages__has_key=user_property)
            # to include CommCareUser creation from UI where a location can be assigned as a part of user creation
            # which is tracked only under "changes" and not "change messages"
            if user_property == LOCATION_FIELD:
                query_filters = query_filters | Q(changes__has_key='location_id')
        else:
            query_filters = Q(changes__has_key=user_property)
        return query_filters

    @property
    def rows(self):
        records = self._get_queryset().order_by(self.ordering)[
            self.pagination.start:self.pagination.start + self.pagination.count
        ]
        for record in records:
            yield self._user_history_row(record, self.domain, self.timezone, False)

    # Override parent method to add new lines to cell values of certain columns
    @property
    def export_rows(self):
        records = self._get_queryset().order_by(self.ordering)
        for record in records:
            yield self._user_history_row(record, self.domain, self.timezone, True)

    @property
    def ordering(self):
        by, direction = list(self.get_sorting_block()[0].items())[0]
        return '-' + by if direction == 'desc' else by

    @memoized
    def _get_location_name(self, location_id):
        from corehq.apps.locations.models import SQLLocation
        if not location_id:
            return None
        try:
            location_object = SQLLocation.objects.get(location_id=location_id)
        except ObjectDoesNotExist:
            return None
        return location_object.display_name

    def _user_history_row(self, record, domain, timezone, for_export):
        if for_export:
            change_messages = ", \n".join(list(get_messages(record.change_messages)))
        else:
            change_messages = self._html_list(list(get_messages(record.change_messages)))
        return [
            record.user_repr,
            record.changed_by_repr,
            _get_action_display(record.action),
            record.changed_via,
            self._user_history_details_cell(record.changes, domain, for_export),
            change_messages,
            ServerTime(record.changed_at).user_time(timezone).ui_string(USER_DATETIME_FORMAT),
        ]

    def _html_list(self, changes):
        items = []
        if isinstance(changes, dict):
            for key, value in changes.items():
                if isinstance(value, dict):
                    value = self._html_list(value)
                elif isinstance(value, list):
                    value = format_html(", ".join(value))
                else:
                    value = format_html(str(value))
                items.append("
{}: {}
".format(key, value))
        elif isinstance(changes, list):
            items = ["
{}
".format(format_html(change)) for change in changes]
        return mark_safe(f"
{''.join(items)}
")

    def _user_history_details_cell(self, changes, domain, for_export):
        properties = UserHistoryReport.get_primary_properties(domain)
        properties.pop("user_data", None)
        primary_changes = {}
        all_changes = {}

        for key, value in changes.items():
            if key == 'location_id':
                value = self._get_location_name(value)
                primary_changes[properties[LOCATION_FIELD]] = value
                all_changes[properties[LOCATION_FIELD]] = value
            elif key == 'user_data':
                for user_data_key, user_data_value in changes['user_data'].items():
                    all_changes[f"user data: {user_data_key}"] = user_data_value
            elif key in properties:
                primary_changes[properties[key]] = value
                all_changes[properties[key]] = value
        more_count = len(all_changes) - len(primary_changes)

        if for_export:
            # This just adds a comma and newline between each change
            return ", \n".join(
                [f"{key}: {value or _('None')}" for key, value in list(all_changes.items())]
            )
        else:
            return render_to_string("reports/standard/partials/user_history_changes.html", {
                "primary_changes": self._html_list(primary_changes),
                "all_changes": self._html_list(all_changes),
                "more_count": more_count,
            })

def _get_action_display(logged_action):
    action = ugettext_lazy("Updated")
    if logged_action == UserHistory.CREATE:
        action = ugettext_lazy("Added")
    elif logged_action == UserHistory.DELETE:
        action = ugettext_lazy("Deleted")
    return action
<|endoftext|>


<commit_before>"""
Constructing Adaptive Configuration Dialogs using Crowd Data
"""

import os
import logging
import subprocess
from collections import defaultdict
from functools import reduce
from operator import mul

from sortedcontainers import SortedSet, SortedListWithKey

from .freq_table import FrequencyTable
from .assoc_rules import AssociationRuleMiner

def get_version():
    """Get the version string.

    Returns:
        The version string.
    """
    pkg_dir = os.path.dirname(__file__)
    src_dir = os.path.abspath(os.path.join(pkg_dir, os.pardir))
    git_dir = os.path.join(src_dir, ".git")
    try:
        # Try to get the  version string dynamically from Git.
        git_args = ("git", "--work-tree", src_dir, "--git-dir", git_dir,
                    "describe", "--tags", "--dirty")
        with open(os.devnull, "w") as devnull:
            version = subprocess.check_output(git_args, stderr=devnull).strip()
    except subprocess.CalledProcessError:
        # Overwritten by a custom 'python setup.py sdist/build' command.
        version = None
    return version

__version__ = get_version()

class ConfigDialog(object):
    """Adaptive configuration dialog.

    This is the base class of all the configuration dialogs defined in
    the package (not intented to be instantiated directly). It defines
    a common interface followed by the dialogs generated using the
    different ConfigDiagBuilder subclasses.

    The interaction with all subclasses must be as follows. First, the
    reset method should be called to begin at a state where all the
    configuration variables are unknown. Next, a call to the method
    get_next_question will suggest a question, which can be posed to
    the user and the answer should be given as feedback to the dialog
    using the method set_answer. It is possible to ignore the
    suggestion given by the dialog and answer the questions in any
    order. In this case, simply call set_answer and future calls to
    get_next_question will act accordingly.

    The config attribute can be used at any time to retrieve the
    configuration values collected so far. Additionally, the method
    is_complete can be used to check whether all the variables has
    been set.

    Attributes:
        config_values: A list with one entry for each variable,
            containing an enumerable with all the possible values of
            the variable.
        config: The current configuration state, i.e. a dict mapping
            variable indices to their values.
    """

    def __init__(self, config_values):
        """Initialize a new instance.

        Arguments:
            config_values: A list with one entry for each variable,
                containing an enumerable with all the possible values
                of the variable.
        """
        super().__init__()
        self.config_values = config_values
        self.reset()

    def reset(self):
        """Reset the dialog to the initial state.

        In the initial configuration state the value of all the
        variables is unknown. This method must be called before making
        any call to get_next_question or set_answer methods.
        """
        self.config = {}

    def get_next_question(self):
        """Get the question that should be asked next.

        Returns the question that should be asked next to the user,
        according to this configuration dialog. Each question is
        identified by the index of the corresponding variable.

        Returns:
            An integer, the variable index.
        """
        raise NotImplementedError()

    def set_answer(self, var_index, var_value):
        """Set the value of a configuration variable.

        It wil be usually called with a variable index returned right
        before by get_next_question and the answer that the user gave
        to the question.

        Arguments:
            var_index: An integer, the variable index.
            var_value: The value of the variable. It must be one of
                the possible values of the variable in the
                config_values instance attribute.
        """
        if var_index in self.config:
            raise ValueError("Variable #{0} is already set".format(var_index))
        else:
            self.config[var_index] = var_value

    def is_complete(self):
        """Check if the configuration is complete.

        Returns:
            True if the values of all the variables has been set,
            False otherwise.
        """
        return len(self.config) == len(self.config_values)

class ConfigDialogBuilder(object):
    """Adaptive configuration dialog builder.
    """

    def __init__(self, config_sample=None,
                 config_values=None,
                 assoc_rule_algorithm="apriori",
                 assoc_rule_min_support=None,
                 assoc_rule_min_confidence=None):
        """Initialize a new instance.

        Arguments:
            config_sample: A 2-dimensional numpy array containing a
                sample of the configuration variables.
            config_values: A list with one entry for each variable,
                containing an enumerable with all the possible values
                of the variable. If it is not given, it is
                automatically computed from the columns of
                config_sample.
            assoc_rule_algorithm: Algorithm for mining the frequent
                item sets. Possible values are: 'apriori' (default)
                and 'fp-growth'.
            assoc_rule_min_support: Minimum item set support in [0,1].
            assoc_rule_min_confidence: Minimum confidence in [0,1].
        """
        super().__init__()
        self._config_sample = config_sample
        self._freq_tab = FrequencyTable(self._config_sample, cache_size=0)
        if config_values is None:
            config_values = [list(SortedSet(self._config_sample[:, i]))
                             for i in range(self._config_sample.shape[1])]
        self._config_values = config_values
        self._assoc_rule_algorithm = assoc_rule_algorithm
        self._assoc_rule_min_support = assoc_rule_min_support
        self._assoc_rule_min_confidence = assoc_rule_min_confidence
        self._logger = logging.getLogger(self.__class__.__name__)

    def build_dialog():
        """Construct an adaptive configuration dialog.

        Returns:
            An instance of a ConfigDialog subclass.
        """
        raise NotImplementedError()

    def _cond_prob(self, x, y, add_one_smoothing=True):
        # Conditional probability distributionn of x given y in the
        # sample of the configuration variables. By default the
        # frequencies are computed using add-one (Laplace) smoothing.
        z = dict(x.items() | y.items())
        num = self._freq_tab.count_freq(z)
        den = self._freq_tab.count_freq(y)
        if add_one_smoothing:
            num += 1
            x_card = [len(self._config_values[i]) for i in x.keys()]
            den += reduce(mul, x_card)
        prob = num / den
        return prob

    def _mine_rules(self):
        # Mine the association rules.
        self._logger.debug("mining association rules")
        miner = AssociationRuleMiner(self._config_sample)
        rules = miner.mine_assoc_rules(self._assoc_rule_min_support,
                                       self._assoc_rule_min_confidence,
                                       algorithm=self._assoc_rule_algorithm)
        if self._logger.isEnabledFor(logging.DEBUG):
            self._logger.debug("found %d rules", len(rules))
            supp = [rule.support for rule in rules]
            self._logger.debug("support values in [%.2f,%.2f]",
                               min(supp), max(supp))
            conf = [rule.confidence for rule in rules]
            self._logger.debug("confidence values in [%.2f,%.2f]",
                               min(conf), max(conf))
        # Merge rules with the same lhs. If two rules have
        # contradictory rhs, the rule with the greatest confidence
        # takes precedence.
        rule_sort_key = lambda rule: rule.confidence  # by inc. confidence
        rules_dict = defaultdict(lambda: SortedListWithKey(key=rule_sort_key))
        for rule in rules:
            lhs_key = hash(frozenset(rule.lhs.items()))
            rules_dict[lhs_key].add(rule)
        merged_rules = [reduce(self._merge_rules, grouped_rules)
                        for grouped_rules in rules_dict.values()]
        self._logger.debug("turned into %d rules after merging",
                           len(merged_rules))
        self._logger.debug("finished mining association rules")
        # Return the merged rules.
        return merged_rules

    def _merge_rules(self, rule1, rule2):
        # Merge two association rules with the same lhs.
        # rule1 is overwritten and returned.
        rule1.support = None
        rule1.confidence = None
        rule1.rhs.update(rule2.rhs)
        return rule1
<commit_msg>Fix debug messages when no rules are found<commit_after>"""
Constructing Adaptive Configuration Dialogs using Crowd Data
"""

import os
import logging
import subprocess
from collections import defaultdict
from functools import reduce
from operator import mul

from sortedcontainers import SortedSet, SortedListWithKey

from .freq_table import FrequencyTable
from .assoc_rules import AssociationRuleMiner

def get_version():
    """Get the version string.

    Returns:
        The version string.
    """
    pkg_dir = os.path.dirname(__file__)
    src_dir = os.path.abspath(os.path.join(pkg_dir, os.pardir))
    git_dir = os.path.join(src_dir, ".git")
    try:
        # Try to get the  version string dynamically from Git.
        git_args = ("git", "--work-tree", src_dir, "--git-dir", git_dir,
                    "describe", "--tags", "--dirty")
        with open(os.devnull, "w") as devnull:
            version = subprocess.check_output(git_args, stderr=devnull).strip()
    except subprocess.CalledProcessError:
        # Overwritten by a custom 'python setup.py sdist/build' command.
        version = None
    return version

__version__ = get_version()

class ConfigDialog(object):
    """Adaptive configuration dialog.

    This is the base class of all the configuration dialogs defined in
    the package (not intented to be instantiated directly). It defines
    a common interface followed by the dialogs generated using the
    different ConfigDiagBuilder subclasses.

    The interaction with all subclasses must be as follows. First, the
    reset method should be called to begin at a state where all the
    configuration variables are unknown. Next, a call to the method
    get_next_question will suggest a question, which can be posed to
    the user and the answer should be given as feedback to the dialog
    using the method set_answer. It is possible to ignore the
    suggestion given by the dialog and answer the questions in any
    order. In this case, simply call set_answer and future calls to
    get_next_question will act accordingly.

    The config attribute can be used at any time to retrieve the
    configuration values collected so far. Additionally, the method
    is_complete can be used to check whether all the variables has
    been set.

    Attributes:
        config_values: A list with one entry for each variable,
            containing an enumerable with all the possible values of
            the variable.
        config: The current configuration state, i.e. a dict mapping
            variable indices to their values.
    """

    def __init__(self, config_values):
        """Initialize a new instance.

        Arguments:
            config_values: A list with one entry for each variable,
                containing an enumerable with all the possible values
                of the variable.
        """
        super().__init__()
        self.config_values = config_values
        self.reset()

    def reset(self):
        """Reset the dialog to the initial state.

        In the initial configuration state the value of all the
        variables is unknown. This method must be called before making
        any call to get_next_question or set_answer methods.
        """
        self.config = {}

    def get_next_question(self):
        """Get the question that should be asked next.

        Returns the question that should be asked next to the user,
        according to this configuration dialog. Each question is
        identified by the index of the corresponding variable.

        Returns:
            An integer, the variable index.
        """
        raise NotImplementedError()

    def set_answer(self, var_index, var_value):
        """Set the value of a configuration variable.

        It wil be usually called with a variable index returned right
        before by get_next_question and the answer that the user gave
        to the question.

        Arguments:
            var_index: An integer, the variable index.
            var_value: The value of the variable. It must be one of
                the possible values of the variable in the
                config_values instance attribute.
        """
        if var_index in self.config:
            raise ValueError("Variable #{0} is already set".format(var_index))
        else:
            self.config[var_index] = var_value

    def is_complete(self):
        """Check if the configuration is complete.

        Returns:
            True if the values of all the variables has been set,
            False otherwise.
        """
        return len(self.config) == len(self.config_values)

class ConfigDialogBuilder(object):
    """Adaptive configuration dialog builder.
    """

    def __init__(self, config_sample=None,
                 config_values=None,
                 assoc_rule_algorithm="apriori",
                 assoc_rule_min_support=None,
                 assoc_rule_min_confidence=None):
        """Initialize a new instance.

        Arguments:
            config_sample: A 2-dimensional numpy array containing a
                sample of the configuration variables.
            config_values: A list with one entry for each variable,
                containing an enumerable with all the possible values
                of the variable. If it is not given, it is
                automatically computed from the columns of
                config_sample.
            assoc_rule_algorithm: Algorithm for mining the frequent
                item sets. Possible values are: 'apriori' (default)
                and 'fp-growth'.
            assoc_rule_min_support: Minimum item set support in [0,1].
            assoc_rule_min_confidence: Minimum confidence in [0,1].
        """
        super().__init__()
        self._config_sample = config_sample
        self._freq_tab = FrequencyTable(self._config_sample, cache_size=0)
        if config_values is None:
            config_values = [list(SortedSet(self._config_sample[:, i]))
                             for i in range(self._config_sample.shape[1])]
        self._config_values = config_values
        self._assoc_rule_algorithm = assoc_rule_algorithm
        self._assoc_rule_min_support = assoc_rule_min_support
        self._assoc_rule_min_confidence = assoc_rule_min_confidence
        self._logger = logging.getLogger(self.__class__.__name__)

    def build_dialog():
        """Construct an adaptive configuration dialog.

        Returns:
            An instance of a ConfigDialog subclass.
        """
        raise NotImplementedError()

    def _cond_prob(self, x, y, add_one_smoothing=True):
        # Conditional probability distributionn of x given y in the
        # sample of the configuration variables. By default the
        # frequencies are computed using add-one (Laplace) smoothing.
        z = dict(x.items() | y.items())
        num = self._freq_tab.count_freq(z)
        den = self._freq_tab.count_freq(y)
        if add_one_smoothing:
            num += 1
            x_card = [len(self._config_values[i]) for i in x.keys()]
            den += reduce(mul, x_card)
        prob = num / den
        return prob

    def _mine_rules(self):
        # Mine the association rules.
        self._logger.debug("mining association rules")
        miner = AssociationRuleMiner(self._config_sample)
        rules = miner.mine_assoc_rules(self._assoc_rule_min_support,
                                       self._assoc_rule_min_confidence,
                                       algorithm=self._assoc_rule_algorithm)
        if self._logger.isEnabledFor(logging.DEBUG):
            self._logger.debug("found %d rules", len(rules))
            if rules:
                supp = [rule.support for rule in rules]
                self._logger.debug("support values in [%.2f,%.2f]",
                                   min(supp), max(supp))
                conf = [rule.confidence for rule in rules]
                self._logger.debug("confidence values in [%.2f,%.2f]",
                                   min(conf), max(conf))
        # Merge rules with the same lhs. If two rules have
        # contradictory rhs, the rule with the greatest confidence
        # takes precedence.
        rule_sort_key = lambda rule: rule.confidence  # by inc. confidence
        rules_dict = defaultdict(lambda: SortedListWithKey(key=rule_sort_key))
        for rule in rules:
            lhs_key = hash(frozenset(rule.lhs.items()))
            rules_dict[lhs_key].add(rule)
        merged_rules = [reduce(self._merge_rules, grouped_rules)
                        for grouped_rules in rules_dict.values()]
        self._logger.debug("turned into %d rules after merging",
                           len(merged_rules))
        self._logger.debug("finished mining association rules")
        # Return the merged rules.
        return merged_rules

    def _merge_rules(self, rule1, rule2):
        # Merge two association rules with the same lhs.
        # rule1 is overwritten and returned.
        rule1.support = None
        rule1.confidence = None
        rule1.rhs.update(rule2.rhs)
        return rule1
<|endoftext|>


<commit_before>"""
The :mod:`pyeda.util` module contains top-level utilities,
such as fundamental functions and decorators.

Interface Functions:

* :func:`bit_on`
* :func:`clog2`
* :func:`parity`

Decorators:

* :func:`cached_property`
"""

def bit_on(num: int, bit: int) -> int:
    """Return the value of a number's bit position.

    For example, since :math:`42 = 2^1 + 2^3 + 2^5`,
    this function will return 1 in bit positions 1, 3, 5:

    >>> [bit_on(42, i) for i in range(clog2(42))]
    [0, 1, 0, 1, 0, 1]
    """
    return (num >> bit) & 1

def clog2(num: int) -> int:
    r"""Return the ceiling log base two of an integer :math:`\ge 1`.

    This function tells you the minimum dimension of a Boolean space with at
    least N points.

    For example, here are the values of ``clog2(N)`` for :math:`1 \le N < 18`:

    >>> [clog2(n) for n in range(1, 18)]
    [0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5]

    This function is undefined for non-positive integers:

    >>> clog2(0)
    Traceback (most recent call last):
        ...
    ValueError: expected num >= 1
    """
    if num < 1:
        raise ValueError("expected num >= 1")
    accum, shifter = 0, 1
    while num > shifter:
        shifter <<= 1
        accum += 1
    return accum

def parity(num: int) -> int:
    """Return the parity of a non-negative integer.

    For example, here are the parities of the first ten integers:

    >>> [parity(n) for n in range(10)]
    [0, 1, 1, 0, 1, 0, 0, 1, 1, 0]

    This function is undefined for negative integers:

    >>> parity(-1)
    Traceback (most recent call last):
        ...
    ValueError: expected num >= 0
    """
    if num < 0:
        raise ValueError("expected num >= 0")
    par = 0
    while num:
        par ^= (num & 1)
        num >>= 1
    return par

def cached_property(func):
    """Return a cached property calculated by the input function.

    Unlike the ``property`` decorator builtin, this decorator will cache the
    return value in order to avoid repeated calculations.
    This is particularly useful when the property involves some non-trivial
    computation.

    For example, consider a class that models a right triangle.
    The hypotenuse ``c`` will only be calculated once.

    .. code-block:: python

       import math

       class RightTriangle
           def __init__(self, a, b):
               self.a = a
               self.b = b

           @cached_property
           def c(self):
               return math.sqrt(self.a**2 + self.b**2)
    """
    def get(self):
        """this docstring will be over-written by func.__doc__"""
        try:
            return self._property_cache[func]
        except AttributeError:
            self._property_cache = dict()
            prop = self._property_cache[func] = func(self)
            return prop
        except KeyError:
            prop = self._property_cache[func] = func(self)
            return prop
    get.__doc__ = func.__doc__
    return property(get)

<commit_msg>Fix cached_property docstring error<commit_after>"""
The :mod:`pyeda.util` module contains top-level utilities,
such as fundamental functions and decorators.

Interface Functions:

* :func:`bit_on`
* :func:`clog2`
* :func:`parity`

Decorators:

* :func:`cached_property`
"""

def bit_on(num: int, bit: int) -> int:
    """Return the value of a number's bit position.

    For example, since :math:`42 = 2^1 + 2^3 + 2^5`,
    this function will return 1 in bit positions 1, 3, 5:

    >>> [bit_on(42, i) for i in range(clog2(42))]
    [0, 1, 0, 1, 0, 1]
    """
    return (num >> bit) & 1

def clog2(num: int) -> int:
    r"""Return the ceiling log base two of an integer :math:`\ge 1`.

    This function tells you the minimum dimension of a Boolean space with at
    least N points.

    For example, here are the values of ``clog2(N)`` for :math:`1 \le N < 18`:

    >>> [clog2(n) for n in range(1, 18)]
    [0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5]

    This function is undefined for non-positive integers:

    >>> clog2(0)
    Traceback (most recent call last):
        ...
    ValueError: expected num >= 1
    """
    if num < 1:
        raise ValueError("expected num >= 1")
    accum, shifter = 0, 1
    while num > shifter:
        shifter <<= 1
        accum += 1
    return accum

def parity(num: int) -> int:
    """Return the parity of a non-negative integer.

    For example, here are the parities of the first ten integers:

    >>> [parity(n) for n in range(10)]
    [0, 1, 1, 0, 1, 0, 0, 1, 1, 0]

    This function is undefined for negative integers:

    >>> parity(-1)
    Traceback (most recent call last):
        ...
    ValueError: expected num >= 0
    """
    if num < 0:
        raise ValueError("expected num >= 0")
    par = 0
    while num:
        par ^= (num & 1)
        num >>= 1
    return par

def cached_property(func):
    """Return a cached property calculated by the input function.

    Unlike the ``property`` decorator builtin, this decorator will cache the
    return value in order to avoid repeated calculations.
    This is particularly useful when the property involves some non-trivial
    computation.

    For example, consider a class that models a right triangle.
    The hypotenuse ``c`` will only be calculated once.

    .. code-block:: python

       import math

       class RightTriangle:
           def __init__(self, a, b):
               self.a = a
               self.b = b

           @cached_property
           def c(self):
               return math.sqrt(self.a**2 + self.b**2)
    """
    def get(self):
        """this docstring will be over-written by func.__doc__"""
        try:
            return self._property_cache[func]
        except AttributeError:
            self._property_cache = dict()
            prop = self._property_cache[func] = func(self)
            return prop
        except KeyError:
            prop = self._property_cache[func] = func(self)
            return prop
    get.__doc__ = func.__doc__
    return property(get)

<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals

AUTHOR = u'itech001'
AUTHORS = u'itech001'
SITENAME = u'ciandcd'
SITEURL = 'http://www.ciandcd.com'
#SITESUBTITLE = 'continuous integration and continuous delivery'
SITESUBTITLE = '软件持续集成和持续发布 QQ群：172758282，437085002'

PATH = 'content'
TIMEZONE = 'Asia/Shanghai'
DEFAULT_LANG = u'zh'
DEFAULT_PAGINATION = 20
USE_FOLDER_AS_CATEGORY = True
DEFAULT_DATE = 'fs'
DEFAULT_CATEGORY = 'misc'
DISPLAY_PAGES_ON_MENU = True
DISPLAY_CATEGORIES_ON_MENU = True
SUMMARY_MAX_LENGTH = 0
TYPOGRIFY = False
CACHE_CONTENT = True
CACHE_PATH = 'cache'
PDF_GENERATOR = False
#READERS = {'html': None}
PAGE_PATHS = ['pages']
STATIC_PATHS = ['images', 'extra/CNAME','extra/robots.txt',]
# path-specific metadata
EXTRA_PATH_METADATA = {
        'extra/CNAME': {'path': 'CNAME'},
        'extra/robots.txt': {'path': 'robots.txt'},
    }

#theme
#https://github.com/getpelican/pelican-themes/tree/master/gum
THEME = "themes/bootstrap-spacelab"

#disqus
DISQUS_SITENAME = ''

# Feed generation is usually not desired when developing
FEED_ALL_ATOM = None
FEED_ATOM = None
FEED_RSS = 'feeds/rss.xml'
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None
AUTHOR_FEED_ATOM = None
AUTHOR_FEED_RSS = None
FEED_ALL_RSS = 'feeds/all.rss.xml'
CATEGORY_FEED_RSS = 'feeds/%s.rss.xml'

# Blogroll
LINKS = (('itech', 'http://www.cnblogs.com/itech/'),
         )

# Social widget
SOCIAL = (('Github', 'https://github.com/ciandcd'),
          )
#GITHUB_URL = 'https://github.com/ciandcd'
TWITTER_URL = ''
FACEBOOK_URL = ''
GOOGLEPLUS_URL = ''

# Uncomment following line if you want document-relative URLs when developing
RELATIVE_URLS = True

#google analytices
GOOGLE_ANALYTICS_ID = ''
GOOGLE_ANALYTICS_SITENAME = ''
GOOGLE_ANALYTICS_UNIVERSAL = ''

PLUGIN_PATH = ["plugins"]
PLUGINS = ["tag_cloud", "tipue_search"]

#tag_cloud
TAG_CLOUD_STEPS = 4
TAG_CLOUD_MAX_ITEMS = 100
TAG_CLOUD_SORTING = 'random'
<commit_msg>update logo<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals

AUTHOR = u'itech001'
AUTHORS = u'itech001'
SITENAME = u'ciandcd.com'
SITEURL = 'http://www.ciandcd.com'
#SITESUBTITLE = 'continuous integration and continuous delivery'
SITESUBTITLE = '软件持续集成和持续发布 QQ群：172758282，437085002'

PATH = 'content'
TIMEZONE = 'Asia/Shanghai'
DEFAULT_LANG = u'zh'
DEFAULT_PAGINATION = 20
USE_FOLDER_AS_CATEGORY = True
DEFAULT_DATE = 'fs'
DEFAULT_CATEGORY = 'misc'
DISPLAY_PAGES_ON_MENU = True
DISPLAY_CATEGORIES_ON_MENU = True
SUMMARY_MAX_LENGTH = 0
TYPOGRIFY = False
CACHE_CONTENT = True
CACHE_PATH = 'cache'
PDF_GENERATOR = False
#READERS = {'html': None}
PAGE_PATHS = ['pages']
STATIC_PATHS = ['images', 'extra/CNAME','extra/robots.txt',]
# path-specific metadata
EXTRA_PATH_METADATA = {
        'extra/CNAME': {'path': 'CNAME'},
        'extra/robots.txt': {'path': 'robots.txt'},
    }

#theme
#https://github.com/getpelican/pelican-themes/tree/master/gum
THEME = "themes/bootstrap-spacelab"

#disqus
DISQUS_SITENAME = ''

# Feed generation is usually not desired when developing
FEED_ALL_ATOM = None
FEED_ATOM = None
FEED_RSS = 'feeds/rss.xml'
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None
AUTHOR_FEED_ATOM = None
AUTHOR_FEED_RSS = None
FEED_ALL_RSS = 'feeds/all.rss.xml'
CATEGORY_FEED_RSS = 'feeds/%s.rss.xml'

# Blogroll
LINKS = (('itech', 'http://www.cnblogs.com/itech/'),
         )

# Social widget
SOCIAL = (('Github', 'https://github.com/ciandcd'),
          )
#GITHUB_URL = 'https://github.com/ciandcd'
TWITTER_URL = ''
FACEBOOK_URL = ''
GOOGLEPLUS_URL = ''

# Uncomment following line if you want document-relative URLs when developing
RELATIVE_URLS = True

#google analytices
GOOGLE_ANALYTICS_ID = ''
GOOGLE_ANALYTICS_SITENAME = ''
GOOGLE_ANALYTICS_UNIVERSAL = ''

PLUGIN_PATH = ["plugins"]
PLUGINS = ["tag_cloud", "tipue_search"]

#tag_cloud
TAG_CLOUD_STEPS = 4
TAG_CLOUD_MAX_ITEMS = 100
TAG_CLOUD_SORTING = 'random'
<|endoftext|>


<commit_before>def create_symbol_table(filename, absolute_flag):
  from lc3_symbol_table import SymbolTable
  symbol_table = SymbolTable(filename)

  if absolute_flag != 'abs':
    import os
    currentpath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep
    filename = currentpath + filename

  current_line = 0
  with open(filename) as file:
    for line in file:
      line = line.strip()
      if line[0] == '.':
        tokens = line.lower().split()
        if tokens[0] == '.orig':
          current_line = int('0' + tokens[1], 16)
        print('**cmd**')
      elif line[0] == ';':
        print('**was comment**')
      else:
        tokens = line.split()
        if not is_lc3_instruction(tokens[0]):
          symbol_table.add(tokens[0], current_line)
          print('>>label: %s @%s' % (tokens[0], hex(current_line)))
        else:
          print('line %s: %s' % (hex(current_line), line))
        current_line += 1
  return symbol_table

def is_lc3_instruction(token):
  from lc3_keywords import opcodes
  for opcode in opcodes:
    if opcode == token.lower():
      return True
  from lc3_keywords import pseudo_ops
  for opcode in pseudo_ops:
    if opcode == token.lower():
      return True
  return False
<commit_msg>make second parameter optional<commit_after>def create_symbol_table(filename, absolute_flag='rel'):
  from lc3_symbol_table import SymbolTable
  symbol_table = SymbolTable(filename)

  if absolute_flag != 'abs':
    import os
    currentpath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep
    filename = currentpath + filename

  current_line = 0
  with open(filename) as file:
    for line in file:
      line = line.strip()
      if line[0] == '.':
        tokens = line.lower().split()
        if tokens[0] == '.orig':
          current_line = int('0' + tokens[1], 16)
        print('**cmd**')
      elif line[0] == ';':
        print('**was comment**')
      else:
        tokens = line.split()
        if not is_lc3_instruction(tokens[0]):
          symbol_table.add(tokens[0], current_line)
          print('>>label: %s @%s' % (tokens[0], hex(current_line)))
        else:
          print('line %s: %s' % (hex(current_line), line))
        current_line += 1
  return symbol_table

def is_lc3_instruction(token):
  from lc3_keywords import opcodes
  for opcode in opcodes:
    if opcode == token.lower():
      return True
  from lc3_keywords import pseudo_ops
  for opcode in pseudo_ops:
    if opcode == token.lower():
      return True
  return False
<|endoftext|>


<commit_before>
"""
This module holds the necessary definitions to make and find config files which
hold critical data about where different compile-critical directories exist.
"""

import os

from .pathway import path, USER
from .searcher import find
from .extractor import extractMajorMinor
from .definitions import CONFIG_FILE_NAME, VER, DOT_VER

from .tools import read_dict_from_file, write_dict_to_file,\
    get_input, get_choice

class DirectoryError(Exception):
    """A custom error used to denote an error with a system of directories"""
    should_be_guided = "If this is the case, please DO run in guided mode so" \
                       " you can manually choose which one you wish to use"
    no_include_dirs = "No include directory found for this version of Python"
    multiple_include = "Multiple include directories were found for this " \
                       "version of Python. " + should_be_guided

    no_runtime_dirs = "No runtime library search directories were found for" \
                      " this version of Python"
    multiple_runtime_dirs = "Multiple runtime search directories were found " \
                            "for this version of Python. " + should_be_guided

    def __init__(self, *args, **kwargs):
        super(DirectoryError, self).__init__(*args, **kwargs)

INCLUDE_DIRS_KEY = 'include_search_directory'
RUNTIME_DIRS_KEY = 'runtime_search_directory'
RUNTIME_KEY = 'runtime_libraries'

# TODO What about the __file__ attribute
# TODO Make this automatic if 'numpy' is seen in the source code?
def getDirsToInclude(string):
    """
    Given a string of module names, it will return the 'include' directories
    essential to their compilation as long as the module has the conventional
    'get_include' function.
    """
    dirs = []
    a = string.strip()
    obj = a.split('-')

    if len(obj) == 1 and obj[0]:
        for module in obj:
            try:
                exec('import {}'.format(module))
            except ImportError:
                raise FileNotFoundError("The module '{}' does not"
                                        "exist".format(module))
            try:
                dirs.append('-I{}'.format(eval(module).get_include()))
            except AttributeError:
                print(NOT_NEEDED_MESSAGE.format(module))
    return dirs

def purge_configs():
    """
    These will delete any configs found in either the current directory or the
    user's home directory
    """
    user_config = path(CONFIG_FILE_NAME, root=USER)
    inplace_config = path(CONFIG_FILE_NAME)

    if os.path.isfile(user_config):
        os.remove(user_config)

    if os.path.isfile(inplace_config):
        os.remove(inplace_config)

def write_config_file(file_path, data):
    """
    Writes a config data structure (dict for now) to the file path specified
    """
    write_dict_to_file(file_path, data)

def read_config_file(file_path):
    """
    Reads a config data structure (dict for now) from the file path specified
    """
    return read_dict_from_file(file_path)

def find_config_file():
    """
    Returns the path to the config file if found in either the current working
    directory, or the user's home directory. If a config file is not found,
    the function will return None.
    """
    local_config_name = path(CONFIG_FILE_NAME)
    if os.path.isfile(local_config_name):
        return local_config_name
    else:
        user_config_name = path(CONFIG_FILE_NAME, root=USER)
        if os.path.isfile(user_config_name):
            return user_config_name
        else:
            return None

CONFIG_NOT_FOUND = 'cnf'
CONFIG_NOT_VALID = 'cnv'
CONFIG_VALID = 'cv'

def get_config():
    """
    Get the config data structure that is supposed to exist in the form:
        (Status, Data)
    """
    config_path = find_config_file()
    if not config_path:
        return CONFIG_NOT_FOUND, None

    try:
        config_data = read_config_file(config_path)
    except Exception as error:
        return CONFIG_NOT_VALID, error

    return CONFIG_VALID, config_data

BOTH_CONFIGS_EXIST = "Config files exist both in the current directory, and " \
                     "the user's directory. Specifying either would result " \
                     "in one being overwriten\n"

USER_CONFIG_EXISTS = "There is a config file in the user's directory, but " \
                     "not in the current directory; making a config in " \
                     "user's would overwrite the existing one\n"

INPLACE_CONFIG_EXISTS = "There is a config file in the current directory, " \
                        "but not in the user's directory; making a config " \
                        "inplace would overwrite the existing one\n"

NO_CONFIGS_EXIST = "No configs were found, it's safe " \
                   "to make the config file anywhere\n"

COMPLEX_PROMPT = "Where do you want to make the config file?"

def _complex_decision(*, guided):
    user = path(CONFIG_FILE_NAME, root=USER)
    inplace = path(CONFIG_FILE_NAME)

    if os.path.isfile(user):
        if os.path.isfile(inplace):
            code = 0
            default = inplace
        else:
            code = 1
            default = user
    else:
        if os.path.isfile(inplace):
            code = 2
            default = inplace
        else:
            code = 3
            default = user

    if guided:
        # Code used to preface the situation to the user on the command line
        if code == 0:
            print(BOTH_CONFIGS_EXIST)
        elif code == 1:
            print(USER_CONFIG_EXISTS)
        elif code == 2:
            print(INPLACE_CONFIG_EXISTS)
        else:
            print(NO_CONFIGS_EXIST)

        # Get the user's response to said situation ^
        response = get_input(COMPLEX_PROMPT,('user', 'inplace', 'default', ''))

        # Decide what to do based on the user's error checked response
        if response == 'user':
            result = user
        elif response == 'inplace':
            result = inplace
        elif response == 'default':
            result = default
        else:
            exit()
            return
    else:
        result = default

    return result, code

SIMPLE_PROMPT = "Do you want to overwrite '{}'?"

def _simple_decision(directory, *, guided):
    config_name = path(CONFIG_FILE_NAME, root=directory)
    if os.path.isfile(config_name):
        if guided:
            response = get_input(SIMPLE_PROMPT.format(config_name), ('y', 'n'))
            if response == 'n':
                exit()
    return config_name

def _make_config_location(*, guided):
    current = path(CONFIG_FILE_NAME)

    if os.path.isdir(path(USER)):
        if path() == path(USER):
            result = _simple_decision(current, guided=guided)
        else:
            result, code = _complex_decision(guided=guided)
    else:
        result = _simple_decision(current, guided=guided)

    return result

def _check_include_dir_identity(include_path):
    a = VER in include_path or DOT_VER in include_path
    b = 'include' in include_path
    return a and b

def _filter_include_dirs(include_dirs):
    filtered_dirs = []
    for include_path in include_dirs:
        if _check_include_dir_identity(include_path):
            filtered_dirs.append(os.path.dirname(include_path))

    if len(filtered_dirs) > 1:
        refiltered_dirs = []
        for include_path in filtered_dirs:
            if extractMajorMinor(include_path) == [DOT_VER]:
                refiltered_dirs.append(include_path)
        filtered_dirs = refiltered_dirs
    return filtered_dirs

INCLUDE_PROMPT = "Choose one of the listed include directories above (by " \
                 "entering the number), or enter nothing to exit the process"

def _make_include_dirs(*, guided):
    unfiltered_dirs = find('Python.h', content="Py_PYTHON_H")
    print("Unfiltered dirs: '{}'".format(unfiltered_dirs))
    include_dirs = _filter_include_dirs(unfiltered_dirs)
    print("Filtered dirs: '{}'".format(include_dirs))

    if not include_dirs:
        raise DirectoryError(DirectoryError.no_include_dirs)
    elif len(include_dirs) == 1:
        return include_dirs[0]
    elif not guided:
        # Be indecisive if multiple valid directories are found
        raise DirectoryError(DirectoryError.multiple_include)
    else:
        return get_choice(INCLUDE_PROMPT, include_dirs)

def _filter_runtime_dirs(rumtime_dirs):
    filtered_dirs = []
    for include_path in rumtime_dirs:
        filtered_dirs.append(os.path.dirname(include_path))
    return filtered_dirs

RUNTIME_DIRS_PROMPT = "Choose one of the listed runtime search directories " \
                      "above (by entering the number), or enter nothing to " \
                      "exit the process"

def _make_runtime_dirs(*, guided):
    # Dont need to filter on this one
    unfiltered_dirs = find(_make_full_runtime())
    runtime_dirs = _filter_runtime_dirs(unfiltered_dirs)

    if not runtime_dirs:
        raise DirectoryError(DirectoryError.no_runtime_dirs)
    elif len(runtime_dirs) == 1:
        return runtime_dirs[0]
    elif not guided:
        # Be indecisive if multiple valid dirs are found
        raise DirectoryError(DirectoryError.multiple_runtime_dirs)
    else:
        return get_choice(RUNTIME_DIRS_PROMPT, runtime_dirs)

def _make_full_runtime():
    return 'lib' + _make_runtime() + '.a'

def _make_runtime():
    name = 'python' + VER
    return name

def make_config_data(*, guided):
    """
    Makes the data necessary to construct a functional config file
    """
    config_data = {}
    config_data[INCLUDE_DIRS_KEY] = _make_include_dirs(guided=guided)
    config_data[RUNTIME_DIRS_KEY] = _make_runtime_dirs(guided=guided)
    config_data[RUNTIME_KEY] = _make_runtime()

    return config_data

def make_config_file(guided=False):
    """
    Options: --auto, --guided, --manual
    Places for the file: --inplace, --user
    """
    config_path = _make_config_location(guided=guided)

    config_data = make_config_data(guided=guided)

    write_config_file(config_path, config_data)

# TODO Make errors cascade out to the outside (this is why travis !catching it)
def generate_configurations(*, guided=False, fresh_start=False, save=False):
    """
    If a config file is found in the standard locations, it will be loaded and
    the config data would be retuned. If not found, then generate the data on
    the fly, and return it
    """

    if fresh_start:
        purge_configs()

    loaded_status, loaded_data = get_config()
    if loaded_status != CONFIG_VALID:
        if save:
            make_config_file(guided=guided)
            status, config_data = get_config()
        else:
            config_data = make_config_data(guided=guided)
    else:
        config_data = loaded_data

    return config_data

def test():
    from pprint import pprint
    pprint(generate_configurations(fresh_start=True, guided=True, save=True))
<commit_msg>Testing 'runtime' path checking conditions<commit_after>
"""
This module holds the necessary definitions to make and find config files which
hold critical data about where different compile-critical directories exist.
"""

import os

from .pathway import path, USER
from .searcher import find
from .extractor import extractMajorMinor
from .definitions import CONFIG_FILE_NAME, VER, DOT_VER

from .tools import read_dict_from_file, write_dict_to_file,\
    get_input, get_choice

class DirectoryError(Exception):
    """A custom error used to denote an error with a system of directories"""
    should_be_guided = "If this is the case, please DO run in guided mode so" \
                       " you can manually choose which one you wish to use"
    no_include_dirs = "No include directory found for this version of Python"
    multiple_include = "Multiple include directories were found for this " \
                       "version of Python. " + should_be_guided

    no_runtime_dirs = "No runtime library search directories were found for" \
                      " this version of Python"
    multiple_runtime_dirs = "Multiple runtime search directories were found " \
                            "for this version of Python. " + should_be_guided

    def __init__(self, *args, **kwargs):
        super(DirectoryError, self).__init__(*args, **kwargs)

INCLUDE_DIRS_KEY = 'include_search_directory'
RUNTIME_DIRS_KEY = 'runtime_search_directory'
RUNTIME_KEY = 'runtime_libraries'

# TODO What about the __file__ attribute
# TODO Make this automatic if 'numpy' is seen in the source code?
def getDirsToInclude(string):
    """
    Given a string of module names, it will return the 'include' directories
    essential to their compilation as long as the module has the conventional
    'get_include' function.
    """
    dirs = []
    a = string.strip()
    obj = a.split('-')

    if len(obj) == 1 and obj[0]:
        for module in obj:
            try:
                exec('import {}'.format(module))
            except ImportError:
                raise FileNotFoundError("The module '{}' does not"
                                        "exist".format(module))
            try:
                dirs.append('-I{}'.format(eval(module).get_include()))
            except AttributeError:
                print(NOT_NEEDED_MESSAGE.format(module))
    return dirs

def purge_configs():
    """
    These will delete any configs found in either the current directory or the
    user's home directory
    """
    user_config = path(CONFIG_FILE_NAME, root=USER)
    inplace_config = path(CONFIG_FILE_NAME)

    if os.path.isfile(user_config):
        os.remove(user_config)

    if os.path.isfile(inplace_config):
        os.remove(inplace_config)

def write_config_file(file_path, data):
    """
    Writes a config data structure (dict for now) to the file path specified
    """
    write_dict_to_file(file_path, data)

def read_config_file(file_path):
    """
    Reads a config data structure (dict for now) from the file path specified
    """
    return read_dict_from_file(file_path)

def find_config_file():
    """
    Returns the path to the config file if found in either the current working
    directory, or the user's home directory. If a config file is not found,
    the function will return None.
    """
    local_config_name = path(CONFIG_FILE_NAME)
    if os.path.isfile(local_config_name):
        return local_config_name
    else:
        user_config_name = path(CONFIG_FILE_NAME, root=USER)
        if os.path.isfile(user_config_name):
            return user_config_name
        else:
            return None

CONFIG_NOT_FOUND = 'cnf'
CONFIG_NOT_VALID = 'cnv'
CONFIG_VALID = 'cv'

def get_config():
    """
    Get the config data structure that is supposed to exist in the form:
        (Status, Data)
    """
    config_path = find_config_file()
    if not config_path:
        return CONFIG_NOT_FOUND, None

    try:
        config_data = read_config_file(config_path)
    except Exception as error:
        return CONFIG_NOT_VALID, error

    return CONFIG_VALID, config_data

BOTH_CONFIGS_EXIST = "Config files exist both in the current directory, and " \
                     "the user's directory. Specifying either would result " \
                     "in one being overwriten\n"

USER_CONFIG_EXISTS = "There is a config file in the user's directory, but " \
                     "not in the current directory; making a config in " \
                     "user's would overwrite the existing one\n"

INPLACE_CONFIG_EXISTS = "There is a config file in the current directory, " \
                        "but not in the user's directory; making a config " \
                        "inplace would overwrite the existing one\n"

NO_CONFIGS_EXIST = "No configs were found, it's safe " \
                   "to make the config file anywhere\n"

COMPLEX_PROMPT = "Where do you want to make the config file?"

def _complex_decision(*, guided):
    user = path(CONFIG_FILE_NAME, root=USER)
    inplace = path(CONFIG_FILE_NAME)

    if os.path.isfile(user):
        if os.path.isfile(inplace):
            code = 0
            default = inplace
        else:
            code = 1
            default = user
    else:
        if os.path.isfile(inplace):
            code = 2
            default = inplace
        else:
            code = 3
            default = user

    if guided:
        # Code used to preface the situation to the user on the command line
        if code == 0:
            print(BOTH_CONFIGS_EXIST)
        elif code == 1:
            print(USER_CONFIG_EXISTS)
        elif code == 2:
            print(INPLACE_CONFIG_EXISTS)
        else:
            print(NO_CONFIGS_EXIST)

        # Get the user's response to said situation ^
        response = get_input(COMPLEX_PROMPT,('user', 'inplace', 'default', ''))

        # Decide what to do based on the user's error checked response
        if response == 'user':
            result = user
        elif response == 'inplace':
            result = inplace
        elif response == 'default':
            result = default
        else:
            exit()
            return
    else:
        result = default

    return result, code

SIMPLE_PROMPT = "Do you want to overwrite '{}'?"

def _simple_decision(directory, *, guided):
    config_name = path(CONFIG_FILE_NAME, root=directory)
    if os.path.isfile(config_name):
        if guided:
            response = get_input(SIMPLE_PROMPT.format(config_name), ('y', 'n'))
            if response == 'n':
                exit()
    return config_name

def _make_config_location(*, guided):
    current = path(CONFIG_FILE_NAME)

    if os.path.isdir(path(USER)):
        if path() == path(USER):
            result = _simple_decision(current, guided=guided)
        else:
            result, code = _complex_decision(guided=guided)
    else:
        result = _simple_decision(current, guided=guided)

    return result

def _check_include_dir_identity(include_path):
    a = VER in include_path or DOT_VER in include_path
    b = 'include' in include_path
    return a and b

def _filter_include_dirs(include_dirs):
    filtered_dirs = []
    for include_path in include_dirs:
        if _check_include_dir_identity(include_path):
            filtered_dirs.append(os.path.dirname(include_path))

    if len(filtered_dirs) > 1:
        refiltered_dirs = []
        for include_path in filtered_dirs:
            if extractMajorMinor(include_path) == [DOT_VER]:
                refiltered_dirs.append(include_path)
        filtered_dirs = refiltered_dirs
    return filtered_dirs

INCLUDE_PROMPT = "Choose one of the listed include directories above (by " \
                 "entering the number), or enter nothing to exit the process"

def _make_include_dirs(*, guided):
    unfiltered_dirs = find('Python.h', content="Py_PYTHON_H")
    include_dirs = _filter_include_dirs(unfiltered_dirs)

    if not include_dirs:
        raise DirectoryError(DirectoryError.no_include_dirs)
    elif len(include_dirs) == 1:
        return include_dirs[0]
    elif not guided:
        # Be indecisive if multiple valid directories are found
        raise DirectoryError(DirectoryError.multiple_include)
    else:
        return get_choice(INCLUDE_PROMPT, include_dirs)

def _filter_runtime_dirs(runtime_dirs):
    filtered_dirs = []
    for include_path in runtime_dirs:
        filtered_dirs.append(os.path.dirname(include_path))
    return filtered_dirs

RUNTIME_DIRS_PROMPT = "Choose one of the listed runtime search directories " \
                      "above (by entering the number), or enter nothing to " \
                      "exit the process"

def _make_runtime_dirs(*, guided):
    # Dont need to filter on this one
    unfiltered_dirs = find(_make_full_runtime())
    print("Unfiltered: '{}'".format(unfiltered_dirs))
    runtime_dirs = _filter_runtime_dirs(unfiltered_dirs)
    print("Filtered dirs: '{}'".format(runtime_dirs))

    if not runtime_dirs:
        raise DirectoryError(DirectoryError.no_runtime_dirs)
    elif len(runtime_dirs) == 1:
        return runtime_dirs[0]
    elif not guided:
        # Be indecisive if multiple valid dirs are found
        raise DirectoryError(DirectoryError.multiple_runtime_dirs)
    else:
        return get_choice(RUNTIME_DIRS_PROMPT, runtime_dirs)

def _make_full_runtime():
    return 'lib' + _make_runtime() + '.a'

def _make_runtime():
    name = 'python' + VER
    return name

def make_config_data(*, guided):
    """
    Makes the data necessary to construct a functional config file
    """
    config_data = {}
    config_data[INCLUDE_DIRS_KEY] = _make_include_dirs(guided=guided)
    config_data[RUNTIME_DIRS_KEY] = _make_runtime_dirs(guided=guided)
    config_data[RUNTIME_KEY] = _make_runtime()

    return config_data

def make_config_file(guided=False):
    """
    Options: --auto, --guided, --manual
    Places for the file: --inplace, --user
    """
    config_path = _make_config_location(guided=guided)

    config_data = make_config_data(guided=guided)

    write_config_file(config_path, config_data)

# TODO Make errors cascade out to the outside (this is why travis !catching it)
def generate_configurations(*, guided=False, fresh_start=False, save=False):
    """
    If a config file is found in the standard locations, it will be loaded and
    the config data would be retuned. If not found, then generate the data on
    the fly, and return it
    """

    if fresh_start:
        purge_configs()

    loaded_status, loaded_data = get_config()
    if loaded_status != CONFIG_VALID:
        if save:
            make_config_file(guided=guided)
            status, config_data = get_config()
        else:
            config_data = make_config_data(guided=guided)
    else:
        config_data = loaded_data

    return config_data

def test():
    from pprint import pprint
    pprint(generate_configurations(fresh_start=True, guided=True, save=True))
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from osv import fields,osv
import time
import tools

class report_document_user(osv.osv):
    _name = "report.document.user"
    _description = "Files details by Users"
    _auto = False
    _columns = {
        'name': fields.char('Year', size=64,readonly=True),
        'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'),
                                  ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')],'Month',readonly=True),
        'user_id':fields.integer('Owner', readonly=True),
        'user':fields.char('User',size=64,readonly=True),
        'directory': fields.char('Directory',size=64,readonly=True),
        'create_date': fields.datetime('Date Created', readonly=True),
        'change_date': fields.datetime('Modified Date', readonly=True),
        'file_size': fields.integer('File Size', readonly=True),
        'nbr':fields.integer('# of Files', readonly=True),
        'type':fields.char('Directory Type',size=64,readonly=True),
     }
    def init(self, cr):
        tools.drop_view_if_exists(cr, 'report_document_user')
        cr.execute("""
            CREATE OR REPLACE VIEW report_document_user as (
                 SELECT
                     min(f.id) as id,
                     to_char(f.create_date, 'YYYY') as name,
                     to_char(f.create_date, 'MM') as month,
                     f.user_id as user_id,
                     u.name as user,
                     count(*) as nbr,
                     d.name as directory,
                     f.create_date as create_date,
                     f.file_size as file_size,
                     min(d.type) as type,
                     f.write_date as change_date
                 FROM ir_attachment f
                     left join document_directory d on (f.parent_id=d.id and d.name<>'')
                     inner join res_users u on (f.user_id=u.id)
                 group by to_char(f.create_date, 'YYYY'), to_char(f.create_date, 'MM'),d.name,f.parent_id,d.type,f.create_date,f.user_id,f.file_size,u.name,d.type,f.write_date
             )
        """)
report_document_user()

class report_files_partner(osv.osv):
    _name = "report.files.partner"
    _description = "Files details by Partners"
    _auto = False
    _columns = {
        'name': fields.char('Year',size=64,required=False, readonly=True),
        'nbr':fields.integer('# of Files', readonly=True),
        'partner':fields.char('Partner',size=64,readonly=True),
        'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'),
                                  ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')],'Month',readonly=True),
     }
    def init(self, cr):
         tools.drop_view_if_exists(cr, 'report_files_partner')
         cr.execute("""
            CREATE VIEW report_files_partner as (
                select min(f.id) as id,count(*) as nbr,
                       to_char(f.create_date,'YYYY') as name,
                       min(to_char(f.create_date,'MM')) as month,
                       p.name as partner
                from ir_attachment f
                inner join res_partner p
                on (f.partner_id=p.id)
                where f.datas_fname is not null
                group by p.name, to_char(f.create_date,'YYYY')
             )
         """)
report_files_partner()

class report_document_file(osv.osv):
    _name = "report.document.file"
    _description = "Files details by Directory"
    _auto = False
    _columns = {
        'file_size': fields.integer('File Size', readonly=True),
        'nbr':fields.integer('# of Files', readonly=True),
        'month': fields.char('Month', size=24,readonly=True),
     }
    _order = "month"
    def init(self, cr):
        tools.drop_view_if_exists(cr, 'report_document_file')
        cr.execute("""
            create or replace view report_document_file as (
                select min(f.id) as id,
                       count(*) as nbr,
                       min(EXTRACT(MONTH FROM f.create_date)||'-'||to_char(f.create_date,'Month')) as month,
                       sum(f.file_size) as file_size
                from ir_attachment f
                group by EXTRACT(MONTH FROM f.create_date)
             )
        """)

report_document_file()

class report_document_wall(osv.osv):
    _name = "report.document.wall"
    _description = "Users that did not inserted documents since one month"
    _auto = False
    _columns = {
        'name': fields.date('Month', readonly=True),
        'user_id':fields.many2one('res.users', 'Owner',readonly=True),
        'user':fields.char('User',size=64,readonly=True),
        'month': fields.char('Month', size=24,readonly=True),
        'last':fields.datetime('Last Posted Time', readonly=True),
             }

    def init(self, cr):
        tools.drop_view_if_exists(cr, 'report_document_wall')
        cr.execute("""
            create or replace view report_document_wall as (
               select max(f.id) as id,
               to_char(min(f.create_date),'YYYY-MM-DD HH24:MI:SS') as last,
               f.user_id as user_id, f.user_id as user,
               to_char(f.create_date,'Month') as month
               from ir_attachment f
               where f.create_date in (
                   select max(i.create_date)
                   from ir_attachment i
                   inner join res_users u on (i.user_id=u.id)
                   group by i.user_id) group by f.user_id,f.create_date
                   having (CURRENT_DATE - to_date(to_char(f.create_date,'YYYY-MM-DD'),'YYYY-MM-DD')) > 30
             )
        """)
report_document_wall()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

<commit_msg>[IMP] document: improve report_files_partner view<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from osv import fields,osv
import time
import tools

class report_document_user(osv.osv):
    _name = "report.document.user"
    _description = "Files details by Users"
    _auto = False
    _columns = {
        'name': fields.char('Year', size=64,readonly=True),
        'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'),
                                  ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')],'Month',readonly=True),
        'user_id':fields.integer('Owner', readonly=True),
        'user':fields.char('User',size=64,readonly=True),
        'directory': fields.char('Directory',size=64,readonly=True),
        'create_date': fields.datetime('Date Created', readonly=True),
        'change_date': fields.datetime('Modified Date', readonly=True),
        'file_size': fields.integer('File Size', readonly=True),
        'nbr':fields.integer('# of Files', readonly=True),
        'type':fields.char('Directory Type',size=64,readonly=True),
     }
    def init(self, cr):
        tools.drop_view_if_exists(cr, 'report_document_user')
        cr.execute("""
            CREATE OR REPLACE VIEW report_document_user as (
                 SELECT
                     min(f.id) as id,
                     to_char(f.create_date, 'YYYY') as name,
                     to_char(f.create_date, 'MM') as month,
                     f.user_id as user_id,
                     u.name as user,
                     count(*) as nbr,
                     d.name as directory,
                     f.create_date as create_date,
                     f.file_size as file_size,
                     min(d.type) as type,
                     f.write_date as change_date
                 FROM ir_attachment f
                     left join document_directory d on (f.parent_id=d.id and d.name<>'')
                     inner join res_users u on (f.user_id=u.id)
                 group by to_char(f.create_date, 'YYYY'), to_char(f.create_date, 'MM'),d.name,f.parent_id,d.type,f.create_date,f.user_id,f.file_size,u.name,d.type,f.write_date
             )
        """)
report_document_user()

class report_files_partner(osv.osv):
    _name = "report.files.partner"
    _description = "Files details by Partners"
    _auto = False
    _columns = {
        'name': fields.char('Year',size=64,required=False, readonly=True),
        'file_size': fields.integer('File Size', readonly=True),
        'nbr':fields.integer('# of Files', readonly=True),
        'partner':fields.char('Partner',size=64,readonly=True),
        'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'),
                                  ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')],'Month',readonly=True),
     }
    def init(self, cr):
         tools.drop_view_if_exists(cr, 'report_files_partner')
         cr.execute("""
            CREATE VIEW report_files_partner as (
                SELECT min(f.id) AS id,
                       COUNT(*) AS nbr,
                       to_char(date_trunc('month', f.create_date),'YYYY') AS name,
                       to_char(date_trunc('month', f.create_date),'MM') AS month,
                       SUM(f.file_size) AS file_size,
                       p.name AS partner

                FROM ir_attachment f
                  LEFT JOIN res_partner p ON (f.partner_id=p.id)
                WHERE f.datas_fname IS NOT NULL
                GROUP BY p.name, date_trunc('month', f.create_date)
             )
         """)
report_files_partner()

class report_document_file(osv.osv):
    _name = "report.document.file"
    _description = "Files details by Directory"
    _auto = False
    _columns = {
        'file_size': fields.integer('File Size', readonly=True),
        'nbr':fields.integer('# of Files', readonly=True),
        'month': fields.char('Month', size=24,readonly=True),
     }
    _order = "month"
    def init(self, cr):
        tools.drop_view_if_exists(cr, 'report_document_file')
        cr.execute("""
            create or replace view report_document_file as (
                select min(f.id) as id,
                       count(*) as nbr,
                       min(EXTRACT(MONTH FROM f.create_date)||'-'||to_char(f.create_date,'Month')) as month,
                       sum(f.file_size) as file_size
                from ir_attachment f
                group by EXTRACT(MONTH FROM f.create_date)
             )
        """)

report_document_file()

class report_document_wall(osv.osv):
    _name = "report.document.wall"
    _description = "Users that did not inserted documents since one month"
    _auto = False
    _columns = {
        'name': fields.date('Month', readonly=True),
        'user_id':fields.many2one('res.users', 'Owner',readonly=True),
        'user':fields.char('User',size=64,readonly=True),
        'month': fields.char('Month', size=24,readonly=True),
        'last':fields.datetime('Last Posted Time', readonly=True),
             }

    def init(self, cr):
        tools.drop_view_if_exists(cr, 'report_document_wall')
        cr.execute("""
            create or replace view report_document_wall as (
               select max(f.id) as id,
               to_char(min(f.create_date),'YYYY-MM-DD HH24:MI:SS') as last,
               f.user_id as user_id, f.user_id as user,
               to_char(f.create_date,'Month') as month
               from ir_attachment f
               where f.create_date in (
                   select max(i.create_date)
                   from ir_attachment i
                   inner join res_users u on (i.user_id=u.id)
                   group by i.user_id) group by f.user_id,f.create_date
                   having (CURRENT_DATE - to_date(to_char(f.create_date,'YYYY-MM-DD'),'YYYY-MM-DD')) > 30
             )
        """)
report_document_wall()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

<|endoftext|>


<commit_before>#!/usr/bin/env python
# encoding: utf-8
#
# Copyright (c) 2015 Ministerio de Fomento
#                    Instituto de Ciencias de la Construcción Eduardo Torroja (IETcc-CSIC)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""Cálculo de la eficiencia energética de los edificios según ISO/DIS 52000-1:2005"""

import argparse
import sys
from .settings import K_EXP, K_RDEL, FACTORESDEPASOOFICIALES
from .energycalculations import weighted_energy
from .inputoutput import readenergyfile, readfactors, ep2string

def main():
    from .__init__ import __version__
    COPY = u"""\tversión: %s
\t(c) 2015-2016 Ministerio de Fomento
\t    2015-2016 Instituto de Ciencias de la Construcción Eduardo Torroja (IETcc-CSIC)
\tAutores: Daniel Jiménez González <danielj@ietcc.csic.es>
\t         Rafael Villar Burke <pachi@ietcc.csic.es>
""" % __version__
    parser = argparse.ArgumentParser(description=u'Cálculo de la eficiencia energética según ISO/DIS 52000-1:2015 y CTE DB-HE',
                                     usage=u"%(prog)s [-h] [-f [FPFILE]] [--krdel [KRDEL]] [--kexp [KEXP]] vecfile\n\n" + COPY,
                                     formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument(dest='vecfile', nargs='?',
                        type=argparse.FileType('r'),
                        help=u'archivo de datos de los vectores energéticos')
    parser.add_argument('-f', '--factores', dest='fpfile', nargs='?',
                        type=argparse.FileType('r'), default=None,
                        help=u'archivo de definición de los factores de paso')
    parser.add_argument('--krdel', nargs='?', default=None, type=float,
                        help=u'factor de resuministro (k_rdel)')
    parser.add_argument('--kexp', nargs='?', default=None, type=float,
                        help=u'factor de exportacion (k_exp)')
    parser.add_argument('-A', '--area', dest='area', nargs='?',
                        type=float, default=1.0,
                        help=u'superficie de referencia')
    parser.add_argument('-o', '--outfile', dest='outputfile', nargs='?',
                        type=argparse.FileType('w'), default=None,
                        help=u'archivo de salida de resultados')
    args = parser.parse_args()

    if not args.vecfile:
        parser.print_help()
        sys.exit(2)

    if args.krdel is None:
        print(u'Usando factor de resuministro (k_rdel) predeterminado')
    k_rdel = K_RDEL if args.krdel is None else args.krdel

    if args.kexp is None:
        print(u'Usando factor de exportación (k_exp) predeterminado')
    k_exp  = K_EXP if args.kexp is None else args.kexp

    if args.fpfile:
        fpdatafile = args.fpfile
        print(u'Archivo de factores de paso: ', fpdatafile.name, '\n')
    else:
        fpdatafile = None
        print(u'Usando factores de paso predeterminados')
    fP = (FACTORESDEPASOOFICIALES if args.fpfile is None
          else readfactors(args.fpfile))

    print(u'Superficie de referencia: %.2f' % args.area)

    data = readenergyfile(args.vecfile.name)
    EP = weighted_energy(data, k_rdel, fP, k_exp)

    cadenasalida = ['%s\n' % args.vecfile.name,
                    ep2string(EP, args.area)]
    print(''.join(cadenasalida))

    if args.outputfile:
        print('Guardando resultados en el archivo:', args.outputfile.name)
        args.outputfile.writelines(cadenasalida)

if __name__ == '__main__':
    main()
<commit_msg>Mejora CLI<commit_after>#!/usr/bin/env python
# encoding: utf-8
#
# Copyright (c) 2015 Ministerio de Fomento
#                    Instituto de Ciencias de la Construcción Eduardo Torroja (IETcc-CSIC)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""Cálculo de la eficiencia energética de los edificios según ISO/DIS 52000-1:2005"""

import argparse
import sys
from .settings import K_EXP, K_RDEL, FACTORESDEPASOOFICIALES
from .energycalculations import weighted_energy
from .inputoutput import readenergyfile, readfactors, ep2string

def main():
    from .__init__ import __version__
    COPY = u"""\tversión: %s
\t(c) 2015-2016 Ministerio de Fomento
\t    2015-2016 Instituto de Ciencias de la Construcción Eduardo Torroja (IETcc-CSIC)
\tAutores: Daniel Jiménez González <danielj@ietcc.csic.es>
\t         Rafael Villar Burke <pachi@ietcc.csic.es>
""" % __version__
    parser = argparse.ArgumentParser(description=u'Cálculo de la eficiencia energética según ISO/DIS 52000-1:2015 y CTE DB-HE',
                                     usage=u"%(prog)s [-h] [-f [FPFILE]] [--krdel [KRDEL]] [--kexp [KEXP]] vecfile\n\n" + COPY,
                                     formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument(dest='vecfile', nargs='?',
                        type=argparse.FileType('r'),
                        help=u'archivo de datos de los vectores energéticos')
    parser.add_argument('-f', '--factores', dest='fpfile', nargs='?',
                        type=argparse.FileType('r'), default=None,
                        help=u'archivo de definición de los factores de paso')
    parser.add_argument('--krdel', nargs='?', default=None, type=float,
                        help=u'factor de resuministro (k_rdel)')
    parser.add_argument('--kexp', nargs='?', default=None, type=float,
                        help=u'factor de exportacion (k_exp)')
    parser.add_argument('-A', '--area', dest='area', nargs='?',
                        type=float, default=1.0,
                        help=u'superficie de referencia')
    parser.add_argument('-o', '--outfile', dest='outputfile', nargs='?',
                        type=argparse.FileType('w'), default=None,
                        help=u'archivo de salida de resultados')
    args = parser.parse_args()

    cadenasalida = []

    if not args.vecfile:
        parser.print_help()
        sys.exit(2)
    cadenasalida.append(u'%s' % args.vecfile.name)

    if args.krdel is None:
        cadenasalida.append(u'Usando factor de resuministro predeterminado (k_rdel = %.2f)' % K_RDEL)
    else:
        cadenasalida.append(u'Usando factor de resuministro krdel = %.2f' % args.krdel)
    k_rdel = K_RDEL if args.krdel is None else args.krdel

    if args.kexp is None:
        cadenasalida.append(u'Usando factor de exportación predeterminado (k_exp = %.2f)' % K_EXP)
    else:
        cadenasalida.append(u'Usando factor de exportación kexp = %.2f' % args.kexp)
    k_exp  = K_EXP if args.kexp is None else args.kexp

    if args.fpfile:
        fpdatafile = args.fpfile
        cadenasalida.append(u'Archivo de factores de paso: %s ' % fpdatafile.name)
    else:
        fpdatafile = None
        cadenasalida.append(u'Usando factores de paso predefinidos')
    fP = (FACTORESDEPASOOFICIALES if args.fpfile is None
          else readfactors(args.fpfile))

    cadenasalida.append(u'Superficie de referencia: %.2f' % args.area)

    data = readenergyfile(args.vecfile.name)
    EP = weighted_energy(data, k_rdel, fP, k_exp)

    cadenasalida.append(ep2string(EP, args.area))
    cadenasalida = u'\n'.join(cadenasalida)
    print(cadenasalida)

    if args.outputfile:
        print(u'Guardando resultados en el archivo: %s' % args.outputfile.name)
        args.outputfile.write(cadenasalida.encode('utf-8'))

if __name__ == '__main__':
    main()
<|endoftext|>