<commit_before># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import locale
import logging
import re
from operator import itemgetter

from odoo import api, fields, models, tools, _
from odoo.tools.safe_eval import safe_eval as eval
from odoo.exceptions import UserError, ValidationError

_logger = logging.getLogger(__name__)

DEFAULT_DATE_FORMAT = '%m/%d/%Y'
DEFAULT_TIME_FORMAT = '%H:%M:%S'

class Lang(models.Model):
    _name = "res.lang"
    _description = "Languages"
    _order = "active desc,name"

    _disallowed_datetime_patterns = tools.DATETIME_FORMATS_MAP.keys()
    _disallowed_datetime_patterns.remove('%y') # this one is in fact allowed, just not good practice

    name = fields.Char(required=True)
    code = fields.Char(string='Locale Code', required=True, help='This field is used to set/get locales for user')
    iso_code = fields.Char(string='ISO code', help='This ISO code is the name of po files to use for translations')
    translatable = fields.Boolean()
    active = fields.Boolean()
    direction = fields.Selection([('ltr', 'Left-to-Right'), ('rtl', 'Right-to-Left')], required=True, default='ltr')
    date_format = fields.Char(string='Date Format', required=True, default=DEFAULT_DATE_FORMAT)
    time_format = fields.Char(string='Time Format', required=True, default=DEFAULT_TIME_FORMAT)
    grouping = fields.Char(string='Separator Format', required=True, default='[]',
        help="The Separator Format should be like [,n] where 0 < n :starting from Unit digit. "
             "-1 will end the separation. e.g. [3,2,-1] will represent 106500 to be 1,06,500; "
             "[1,2,-1] will represent it to be 106,50,0;[3] will represent it as 106,500. "
             "Provided ',' as the thousand separator in each case.")
    decimal_point = fields.Char(string='Decimal Separator', required=True, default='.')
    thousands_sep = fields.Char(string='Thousands Separator', default=',')

    _sql_constraints = [
        ('name_uniq', 'unique(name)', 'The name of the language must be unique !'),
        ('code_uniq', 'unique(code)', 'The code of the language must be unique !'),
    ]

    @api.constrains('active')
    def _check_active(self):
        # do not check during installation
        if self.env.registry.ready and not self.search_count([]):
            raise ValidationError(_('At least one language must be active.'))

    @api.constrains('time_format', 'date_format')
    def _check_format(self):
        for lang in self:
            for pattern in lang._disallowed_datetime_patterns:
                if (lang.time_format and pattern in lang.time_format) or \
                        (lang.date_format and pattern in lang.date_format):
                    raise ValidationError(_('Invalid date/time format directive specified. '
                                            'Please refer to the list of allowed directives, '
                                            'displayed when you edit a language.'))

    @api.constrains('grouping')
    def _check_grouping(self):
        warning = _('The Separator Format should be like [,n] where 0 < n :starting from Unit digit. '
                    '-1 will end the separation. e.g. [3,2,-1] will represent 106500 to be 1,06,500;'
                    '[1,2,-1] will represent it to be 106,50,0;[3] will represent it as 106,500. '
                    'Provided as the thousand separator in each case.')
        for lang in self:
            try:
                if not all(isinstance(x, int) for x in eval(lang.grouping)):
                    raise ValidationError(warning)
            except Exception:
                raise ValidationError(warning)

    @api.model_cr
    def _register_hook(self):
        # check that there is at least one active language
        if not self.search_count([]):
            _logger.error("No language is active.")

    @api.model
    def load_lang(self, lang, lang_name=None):
        """ Create the given language if necessary, and make it active. """
        # if the language exists, simply make it active
        language = self.with_context(active_test=False).search([('code', '=', lang)], limit=1)
        if language:
            language.write({'active': True})
            return language.id

        # create the language with locale information
        fail = True
        iso_lang = tools.get_iso_codes(lang)
        for ln in tools.get_locales(lang):
            try:
                locale.setlocale(locale.LC_ALL, str(ln))
                fail = False
                break
            except locale.Error:
                continue
        if fail:
            lc = locale.getdefaultlocale()[0]
            msg = 'Unable to get information for locale %s. Information from the default locale (%s) have been used.'
            _logger.warning(msg, lang, lc)

        if not lang_name:
            lang_name = lang

        def fix_xa0(s):
            """Fix badly-encoded non-breaking space Unicode character from locale.localeconv(),
               coercing to utf-8, as some platform seem to output localeconv() in their system
               encoding, e.g. Windows-1252"""
            if s == '\xa0':
                return '\xc2\xa0'
            return s

        def fix_datetime_format(format):
            """Python's strftime supports only the format directives
               that are available on the platform's libc, so in order to
               be 100% cross-platform we map to the directives required by
               the C standard (1989 version), always available on platforms
               with a C standard implementation."""
            # For some locales, nl_langinfo returns a D_FMT/T_FMT that contains
            # unsupported '%-' patterns, e.g. for cs_CZ
            format = format.replace('%-', '%')
            for pattern, replacement in tools.DATETIME_FORMATS_MAP.iteritems():
                format = format.replace(pattern, replacement)
            return str(format)

        conv = locale.localeconv()
        lang_info = {
            'code': lang,
            'iso_code': iso_lang,
            'name': lang_name,
            'active': True,
            'translatable': True,
            'date_format' : fix_datetime_format(locale.nl_langinfo(locale.D_FMT)),
            'time_format' : fix_datetime_format(locale.nl_langinfo(locale.T_FMT)),
            'decimal_point' : fix_xa0(str(conv['decimal_point'])),
            'thousands_sep' : fix_xa0(str(conv['thousands_sep'])),
            'grouping' : str(conv.get('grouping', [])),
        }
        try:
            return self.create(lang_info).id
        finally:
            tools.resetlocale()

    @api.model
    def install_lang(self):
        """

        This method is called from openerp/addons/base/base_data.xml to load
        some language and set it as the default for every partners. The
        language is set via tools.config by the RPC 'create' method on the
        'db' object. This is a fragile solution and something else should be
        found.

        """
        # config['load_language'] is a comma-separated list or None
        lang_code = (tools.config.get('load_language') or 'en_US').split(',')[0]
        lang = self.search([('code', '=', lang_code)])
        if not lang:
            self.load_lang(lang_code)
        IrValues = self.env['ir.values']
        default_value = IrValues.get('default', False, ['res.partner'])
        if not default_value:
            IrValues.set('default', False, 'lang', ['res.partner'], lang_code)
            # set language of main company, created directly by db bootstrap SQL
            partner = self.env.user.company_id.partner_id
            if not partner.lang:
                partner.write({'lang': lang_code})
        return True

    @api.model
    @api.returns('self', lambda value: value.id)
    @tools.ormcache('code')
    def _lang_get(self, code):
        return (self.search([('code', '=', code)]) or
                self.search([('code', '=', 'en_US')]) or
                self.search([], limit=1))

    @api.v7
    def _lang_data_get(self, cr, uid, lang, monetary=False):
        if isinstance(lang, basestring):
            lang = self._lang_get(cr, uid, lang)
        return self.browse(cr, uid, lang)._data_get(monetary)

    @tools.ormcache('self.code', 'monetary')
    def _data_get(self, monetary=False):
        conv = locale.localeconv()
        thousands_sep = self.thousands_sep or conv[monetary and 'mon_thousands_sep' or 'thousands_sep']
        decimal_point = self.decimal_point
        grouping = self.grouping
        return grouping, thousands_sep, decimal_point

    @api.model
    @tools.ormcache()
    def get_available(self):
        """ Return the available languages as a list of (code, name) sorted by name. """
        langs = self.with_context(active_test=False).search([])
        return sorted([(lang.code, lang.name) for lang in langs], key=itemgetter(1))

    @api.model
    @tools.ormcache()
    def get_installed(self):
        """ Return the installed languages as a list of (code, name) sorted by name. """
        langs = self.with_context(active_test=True).search([])
        return sorted([(lang.code, lang.name) for lang in langs], key=itemgetter(1))

    @api.model
    def create(self, vals):
        self.clear_caches()
        return super(Lang, self).create(vals)

    @api.multi
    def write(self, vals):
        lang_codes = self.mapped('code')
        if 'code' in vals and any(code != vals['code'] for code in lang_codes):
            raise UserError(_("Language code cannot be modified."))
        if vals.get('active') == False and self.env['res.users'].search([('lang', 'in', lang_codes)]):
            raise UserError(_("Cannot unactivate a language that is currently used by users."))
        self.clear_caches()
        return super(Lang, self).write(vals)

    @api.multi
    def unlink(self):
        for language in self:
            if language.code == 'en_US':
                raise UserError(_("Base Language 'en_US' can not be deleted!"))
            ctx_lang = self._context.get('lang')
            if ctx_lang and (language.code == ctx_lang):
                raise UserError(_("You cannot delete the language which is User's Preferred Language!"))
            if language.active:
                raise UserError(_("You cannot delete the language which is Active!\nPlease de-activate the language first."))
            self.env['ir.translation'].search([('lang', '=', language.code)]).unlink()
        self.clear_caches()
        return super(Lang, self).unlink()

    #
    # IDS: can be a list of IDS or a list of XML_IDS
    #
    @api.v7
    def format(self, cr, uid, ids, percent, value, grouping=False, monetary=False, context=None):
        # Refering the old code, `ids` is expected to have only one value(ID or XML_ID) inside list, hence used ids[0].
        lang_id = ids[0]
        if isinstance(lang_id, (str, unicode)):
            lang_id = self._lang_get(cr, uid, lang_id)
        lang = self.browse(cr, uid, lang_id, context=context)
        return Lang.format(lang, percent, value, grouping=grouping, monetary=monetary)

    @api.v8
    def format(self, percent, value, grouping=False, monetary=False):
        """ Format() will return the language-specific output for float values"""
        self.ensure_one()
        if percent[0] != '%':
            raise ValueError(_("format() must be given exactly one %char format specifier"))

        formatted = percent % value

        # floats and decimal ints need special action!
        if grouping:
            lang_grouping, thousands_sep, decimal_point = self._data_get(monetary)
            eval_lang_grouping = eval(lang_grouping)

            if percent[-1] in 'eEfFgG':
                parts = formatted.split('.')
                parts[0], _ = intersperse(parts[0], eval_lang_grouping, thousands_sep)

                formatted = decimal_point.join(parts)

            elif percent[-1] in 'diu':
                formatted = intersperse(formatted, eval_lang_grouping, thousands_sep)[0]

        return formatted

def split(l, counts):
    """

    >>> split("hello world", [])
    ['hello world']
    >>> split("hello world", [1])
    ['h', 'ello world']
    >>> split("hello world", [2])
    ['he', 'llo world']
    >>> split("hello world", [2,3])
    ['he', 'llo', ' world']
    >>> split("hello world", [2,3,0])
    ['he', 'llo', ' wo', 'rld']
    >>> split("hello world", [2,-1,3])
    ['he', 'llo world']

    """
    res = []
    saved_count = len(l) # count to use when encoutering a zero
    for count in counts:
        if not l:
            break
        if count == -1:
            break
        if count == 0:
            while l:
                res.append(l[:saved_count])
                l = l[saved_count:]
            break
        res.append(l[:count])
        l = l[count:]
        saved_count = count
    if l:
        res.append(l)
    return res

intersperse_pat = re.compile('([^0-9]*)([^ ]*)(.*)')

def intersperse(string, counts, separator=''):
    """

    See the asserts below for examples.

    """
    left, rest, right = intersperse_pat.match(string).groups()
    def reverse(s): return s[::-1]
    splits = split(reverse(rest), counts)
    res = separator.join(map(reverse, reverse(splits)))
    return left + res + right, len(splits) > 0 and len(splits) -1 or 0
<commit_msg>[FIX] res_lang: do not `ormcache` a method that returns a record<commit_after># -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import locale
import logging
import re
from operator import itemgetter

from odoo import api, fields, models, tools, _
from odoo.tools.safe_eval import safe_eval as eval
from odoo.exceptions import UserError, ValidationError

_logger = logging.getLogger(__name__)

DEFAULT_DATE_FORMAT = '%m/%d/%Y'
DEFAULT_TIME_FORMAT = '%H:%M:%S'

class Lang(models.Model):
    _name = "res.lang"
    _description = "Languages"
    _order = "active desc,name"

    _disallowed_datetime_patterns = tools.DATETIME_FORMATS_MAP.keys()
    _disallowed_datetime_patterns.remove('%y') # this one is in fact allowed, just not good practice

    name = fields.Char(required=True)
    code = fields.Char(string='Locale Code', required=True, help='This field is used to set/get locales for user')
    iso_code = fields.Char(string='ISO code', help='This ISO code is the name of po files to use for translations')
    translatable = fields.Boolean()
    active = fields.Boolean()
    direction = fields.Selection([('ltr', 'Left-to-Right'), ('rtl', 'Right-to-Left')], required=True, default='ltr')
    date_format = fields.Char(string='Date Format', required=True, default=DEFAULT_DATE_FORMAT)
    time_format = fields.Char(string='Time Format', required=True, default=DEFAULT_TIME_FORMAT)
    grouping = fields.Char(string='Separator Format', required=True, default='[]',
        help="The Separator Format should be like [,n] where 0 < n :starting from Unit digit. "
             "-1 will end the separation. e.g. [3,2,-1] will represent 106500 to be 1,06,500; "
             "[1,2,-1] will represent it to be 106,50,0;[3] will represent it as 106,500. "
             "Provided ',' as the thousand separator in each case.")
    decimal_point = fields.Char(string='Decimal Separator', required=True, default='.')
    thousands_sep = fields.Char(string='Thousands Separator', default=',')

    _sql_constraints = [
        ('name_uniq', 'unique(name)', 'The name of the language must be unique !'),
        ('code_uniq', 'unique(code)', 'The code of the language must be unique !'),
    ]

    @api.constrains('active')
    def _check_active(self):
        # do not check during installation
        if self.env.registry.ready and not self.search_count([]):
            raise ValidationError(_('At least one language must be active.'))

    @api.constrains('time_format', 'date_format')
    def _check_format(self):
        for lang in self:
            for pattern in lang._disallowed_datetime_patterns:
                if (lang.time_format and pattern in lang.time_format) or \
                        (lang.date_format and pattern in lang.date_format):
                    raise ValidationError(_('Invalid date/time format directive specified. '
                                            'Please refer to the list of allowed directives, '
                                            'displayed when you edit a language.'))

    @api.constrains('grouping')
    def _check_grouping(self):
        warning = _('The Separator Format should be like [,n] where 0 < n :starting from Unit digit. '
                    '-1 will end the separation. e.g. [3,2,-1] will represent 106500 to be 1,06,500;'
                    '[1,2,-1] will represent it to be 106,50,0;[3] will represent it as 106,500. '
                    'Provided as the thousand separator in each case.')
        for lang in self:
            try:
                if not all(isinstance(x, int) for x in eval(lang.grouping)):
                    raise ValidationError(warning)
            except Exception:
                raise ValidationError(warning)

    @api.model_cr
    def _register_hook(self):
        # check that there is at least one active language
        if not self.search_count([]):
            _logger.error("No language is active.")

    @api.model
    def load_lang(self, lang, lang_name=None):
        """ Create the given language if necessary, and make it active. """
        # if the language exists, simply make it active
        language = self.with_context(active_test=False).search([('code', '=', lang)], limit=1)
        if language:
            language.write({'active': True})
            return language.id

        # create the language with locale information
        fail = True
        iso_lang = tools.get_iso_codes(lang)
        for ln in tools.get_locales(lang):
            try:
                locale.setlocale(locale.LC_ALL, str(ln))
                fail = False
                break
            except locale.Error:
                continue
        if fail:
            lc = locale.getdefaultlocale()[0]
            msg = 'Unable to get information for locale %s. Information from the default locale (%s) have been used.'
            _logger.warning(msg, lang, lc)

        if not lang_name:
            lang_name = lang

        def fix_xa0(s):
            """Fix badly-encoded non-breaking space Unicode character from locale.localeconv(),
               coercing to utf-8, as some platform seem to output localeconv() in their system
               encoding, e.g. Windows-1252"""
            if s == '\xa0':
                return '\xc2\xa0'
            return s

        def fix_datetime_format(format):
            """Python's strftime supports only the format directives
               that are available on the platform's libc, so in order to
               be 100% cross-platform we map to the directives required by
               the C standard (1989 version), always available on platforms
               with a C standard implementation."""
            # For some locales, nl_langinfo returns a D_FMT/T_FMT that contains
            # unsupported '%-' patterns, e.g. for cs_CZ
            format = format.replace('%-', '%')
            for pattern, replacement in tools.DATETIME_FORMATS_MAP.iteritems():
                format = format.replace(pattern, replacement)
            return str(format)

        conv = locale.localeconv()
        lang_info = {
            'code': lang,
            'iso_code': iso_lang,
            'name': lang_name,
            'active': True,
            'translatable': True,
            'date_format' : fix_datetime_format(locale.nl_langinfo(locale.D_FMT)),
            'time_format' : fix_datetime_format(locale.nl_langinfo(locale.T_FMT)),
            'decimal_point' : fix_xa0(str(conv['decimal_point'])),
            'thousands_sep' : fix_xa0(str(conv['thousands_sep'])),
            'grouping' : str(conv.get('grouping', [])),
        }
        try:
            return self.create(lang_info).id
        finally:
            tools.resetlocale()

    @api.model
    def install_lang(self):
        """

        This method is called from openerp/addons/base/base_data.xml to load
        some language and set it as the default for every partners. The
        language is set via tools.config by the RPC 'create' method on the
        'db' object. This is a fragile solution and something else should be
        found.

        """
        # config['load_language'] is a comma-separated list or None
        lang_code = (tools.config.get('load_language') or 'en_US').split(',')[0]
        lang = self.search([('code', '=', lang_code)])
        if not lang:
            self.load_lang(lang_code)
        IrValues = self.env['ir.values']
        default_value = IrValues.get('default', False, ['res.partner'])
        if not default_value:
            IrValues.set('default', False, 'lang', ['res.partner'], lang_code)
            # set language of main company, created directly by db bootstrap SQL
            partner = self.env.user.company_id.partner_id
            if not partner.lang:
                partner.write({'lang': lang_code})
        return True

    @tools.ormcache('code')
    def _lang_get_id(self, code):
        return (self.search([('code', '=', code)]) or
                self.search([('code', '=', 'en_US')]) or
                self.search([], limit=1)).id

    @api.model
    @api.returns('self', lambda value: value.id)
    def _lang_get(self, code):
        return self.browse(self._lang_get_id(code))

    @api.v7
    def _lang_data_get(self, cr, uid, lang, monetary=False):
        if isinstance(lang, basestring):
            lang = self._lang_get(cr, uid, lang)
        return self.browse(cr, uid, lang)._data_get(monetary)

    @tools.ormcache('self.code', 'monetary')
    def _data_get(self, monetary=False):
        conv = locale.localeconv()
        thousands_sep = self.thousands_sep or conv[monetary and 'mon_thousands_sep' or 'thousands_sep']
        decimal_point = self.decimal_point
        grouping = self.grouping
        return grouping, thousands_sep, decimal_point

    @api.model
    @tools.ormcache()
    def get_available(self):
        """ Return the available languages as a list of (code, name) sorted by name. """
        langs = self.with_context(active_test=False).search([])
        return sorted([(lang.code, lang.name) for lang in langs], key=itemgetter(1))

    @api.model
    @tools.ormcache()
    def get_installed(self):
        """ Return the installed languages as a list of (code, name) sorted by name. """
        langs = self.with_context(active_test=True).search([])
        return sorted([(lang.code, lang.name) for lang in langs], key=itemgetter(1))

    @api.model
    def create(self, vals):
        self.clear_caches()
        return super(Lang, self).create(vals)

    @api.multi
    def write(self, vals):
        lang_codes = self.mapped('code')
        if 'code' in vals and any(code != vals['code'] for code in lang_codes):
            raise UserError(_("Language code cannot be modified."))
        if vals.get('active') == False and self.env['res.users'].search([('lang', 'in', lang_codes)]):
            raise UserError(_("Cannot unactivate a language that is currently used by users."))
        self.clear_caches()
        return super(Lang, self).write(vals)

    @api.multi
    def unlink(self):
        for language in self:
            if language.code == 'en_US':
                raise UserError(_("Base Language 'en_US' can not be deleted!"))
            ctx_lang = self._context.get('lang')
            if ctx_lang and (language.code == ctx_lang):
                raise UserError(_("You cannot delete the language which is User's Preferred Language!"))
            if language.active:
                raise UserError(_("You cannot delete the language which is Active!\nPlease de-activate the language first."))
            self.env['ir.translation'].search([('lang', '=', language.code)]).unlink()
        self.clear_caches()
        return super(Lang, self).unlink()

    #
    # IDS: can be a list of IDS or a list of XML_IDS
    #
    @api.v7
    def format(self, cr, uid, ids, percent, value, grouping=False, monetary=False, context=None):
        # Refering the old code, `ids` is expected to have only one value(ID or XML_ID) inside list, hence used ids[0].
        lang_id = ids[0]
        if isinstance(lang_id, (str, unicode)):
            lang_id = self._lang_get(cr, uid, lang_id)
        lang = self.browse(cr, uid, lang_id, context=context)
        return Lang.format(lang, percent, value, grouping=grouping, monetary=monetary)

    @api.v8
    def format(self, percent, value, grouping=False, monetary=False):
        """ Format() will return the language-specific output for float values"""
        self.ensure_one()
        if percent[0] != '%':
            raise ValueError(_("format() must be given exactly one %char format specifier"))

        formatted = percent % value

        # floats and decimal ints need special action!
        if grouping:
            lang_grouping, thousands_sep, decimal_point = self._data_get(monetary)
            eval_lang_grouping = eval(lang_grouping)

            if percent[-1] in 'eEfFgG':
                parts = formatted.split('.')
                parts[0], _ = intersperse(parts[0], eval_lang_grouping, thousands_sep)

                formatted = decimal_point.join(parts)

            elif percent[-1] in 'diu':
                formatted = intersperse(formatted, eval_lang_grouping, thousands_sep)[0]

        return formatted

def split(l, counts):
    """

    >>> split("hello world", [])
    ['hello world']
    >>> split("hello world", [1])
    ['h', 'ello world']
    >>> split("hello world", [2])
    ['he', 'llo world']
    >>> split("hello world", [2,3])
    ['he', 'llo', ' world']
    >>> split("hello world", [2,3,0])
    ['he', 'llo', ' wo', 'rld']
    >>> split("hello world", [2,-1,3])
    ['he', 'llo world']

    """
    res = []
    saved_count = len(l) # count to use when encoutering a zero
    for count in counts:
        if not l:
            break
        if count == -1:
            break
        if count == 0:
            while l:
                res.append(l[:saved_count])
                l = l[saved_count:]
            break
        res.append(l[:count])
        l = l[count:]
        saved_count = count
    if l:
        res.append(l)
    return res

intersperse_pat = re.compile('([^0-9]*)([^ ]*)(.*)')

def intersperse(string, counts, separator=''):
    """

    See the asserts below for examples.

    """
    left, rest, right = intersperse_pat.match(string).groups()
    def reverse(s): return s[::-1]
    splits = split(reverse(rest), counts)
    res = separator.join(map(reverse, reverse(splits)))
    return left + res + right, len(splits) > 0 and len(splits) -1 or 0
<|endoftext|>


<commit_before>#!/usr/bin/python2.5
#
# Copyright 2009 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Helpers used for list info functions.
"""

__authors__ = [
  '"Lennard de Rijk" <ljvderijk@gmail.com>',
  ]

from django.utils.translation import ugettext

from soc.views.helper import redirects

DEF_NO_RECORD_AVAILABLE_MESSAGE = ugettext('No Record Available')

def getStudentProposalInfo(ranking, proposals_keys):
  """Returns a function that returns information about the rank and assignment.

  Args:
    ranking: dict with a mapping from Student Proposal key to rank
    proposals_keys: list of proposal keys assigned a slot
  """

  def wrapper(item, _):
    """Wrapper method.
    """
    info = {'rank': ranking[item.key()]}

    if item.key() in proposals_keys:
      info['item_class'] =  'selected'
    else:
      info['item_class'] =  'normal'

    return info
  return wrapper

def setStudentProjectSurveyInfo(list_content, program_entity):
  """Sets the list info to a method that returns information used in a Student
  Project table to show how many evaluations have been available and how
  many have been taken.

  Args:
    list_content: list content for which to set the info
    program_entity: the Program to check the total amount of
                    (Grading)ProjctSurveys for

  Returns:
    The original list_content with info set
  """

  from soc.logic.models.survey import grading_logic as grading_survey_logic
  from soc.logic.models.survey import project_logic as project_survey_logic
  from soc.logic.models.survey_record import grading_logic
  from soc.logic.models.survey_record import project_logic

  if not list_content:
    # this can happen because of the need_content parameter for getListContent
    return list_content

  fields = {'scope_path': program_entity.key().id_or_name()}

  # count the number of have been active ProjectSurveys
  project_surveys = project_survey_logic.getForFields(fields)
  project_survey_count = len(project_surveys)

  for project_survey in project_surveys:
    if not project_survey_logic.hasRecord(project_survey):
      project_survey_count = project_survey_count - 1

  # count the number of have been active GradingProjectSurveys
  grading_surveys = grading_survey_logic.getForFields(fields)
  grading_survey_count = len(grading_surveys)

  for grading_survey in grading_surveys:
    if not grading_survey_logic.hasRecord(grading_survey):
      grading_survey_count = grading_survey_count - 1

  # Pre-store the needed info since Django calls the wrapper method for every
  # info call.
  info_storage = {}

  for item in list_content['data']:
    fields = {'project': item}

    # count the amount of records we have on store for this project
    project_record_count = project_logic.getQueryForFields(fields).count()
    grading_record_count = grading_logic.getQueryForFields(fields).count()

    info = {'project_surveys_total': project_survey_count,
            'project_surveys_completed': project_record_count,
            'grading_project_surveys_total': grading_survey_count,
            'grading_project_surveys_completed': grading_record_count}

    info_storage[item.key()] = info

  def wrapper(item, _):
    """Wrapper method.
    """
    return info_storage[item.key()]

  list_content['info'] = (wrapper, None)
  return list_content

def setProjectSurveyInfoForProject(list_content, project_entity,
                                   survey_params):
  """Sets the list info to a function for the given list.

  This function contains the information used for showing ProjectSurvey
  records on the StudentProject manage page.

  Args:
    list_content: list content for which to set the info
    project_entity: a StudentProject entity
    survey_params: params for the view of the type of Survey that is listed
  """

  survey_logic = survey_params['logic']
  record_logic = survey_logic.getRecordLogic()

  # store the needed info since Django calls the wrapper method for every
  # info call.
  info_storage = {}

  for survey_entity in list_content['data']:
    # try to retrieve the SurveyRecord for the given Survey and Project
    fields = {'survey': survey_entity,
              'project': project_entity}
    record_entity = record_logic.getForFields(fields, unique=True)

    info = {'record': record_entity}

    if record_entity:
      # SurveyRecord has been found store the import data in info
      info['taken_by'] = record_entity.user.name
      info['taken_on'] = record_entity.modified
      info['record_url'] = redirects.getViewSurveyRecordRedirect(record_entity,
                                                                 survey_params)
    else:
      info['taken_by'] = DEF_NO_RECORD_AVAILABLE_MESSAGE
      info['taken_on'] = DEF_NO_RECORD_AVAILABLE_MESSAGE

    take_redirect_info = {'survey': survey_entity,
                          'params': survey_params}
    info['take_url'] = redirects.getTakeProjectSurveyRedirect(
        project_entity, take_redirect_info)

    info_storage[survey_entity.key()] = info

  def wrapper(item, _):
    """Wrapper method.
    """
    return info_storage[item.key()]

  list_content['info'] = (wrapper, None)
  return list_content
<commit_msg>Fixed wrong imports in list_info module.<commit_after>#!/usr/bin/python2.5
#
# Copyright 2009 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Helpers used for list info functions.
"""

__authors__ = [
  '"Lennard de Rijk" <ljvderijk@gmail.com>',
  ]

from django.utils.translation import ugettext

from soc.views.helper import redirects

DEF_NO_RECORD_AVAILABLE_MESSAGE = ugettext('No Record Available')

def getStudentProposalInfo(ranking, proposals_keys):
  """Returns a function that returns information about the rank and assignment.

  Args:
    ranking: dict with a mapping from Student Proposal key to rank
    proposals_keys: list of proposal keys assigned a slot
  """

  def wrapper(item, _):
    """Wrapper method.
    """
    info = {'rank': ranking[item.key()]}

    if item.key() in proposals_keys:
      info['item_class'] =  'selected'
    else:
      info['item_class'] =  'normal'

    return info
  return wrapper

def setStudentProjectSurveyInfo(list_content, program_entity):
  """Sets the list info to a method that returns information used in a Student
  Project table to show how many evaluations have been available and how
  many have been taken.

  Args:
    list_content: list content for which to set the info
    program_entity: the Program to check the total amount of
                    (Grading)ProjctSurveys for

  Returns:
    The original list_content with info set
  """

  from soc.modules.gsoc.logic.models.survey import grading_logic as \
      grading_survey_logic
  from soc.modules.gsoc.logic.models.survey import project_logic as \
      project_survey_logic
  from soc.modules.gsoc.logic.models.survey_record import grading_logic
  from soc.modules.gsoc.logic.models.survey_record import project_logic

  if not list_content:
    # this can happen because of the need_content parameter for getListContent
    return list_content

  fields = {'scope_path': program_entity.key().id_or_name()}

  # count the number of have been active ProjectSurveys
  project_surveys = project_survey_logic.getForFields(fields)
  project_survey_count = len(project_surveys)

  for project_survey in project_surveys:
    if not project_survey_logic.hasRecord(project_survey):
      project_survey_count = project_survey_count - 1

  # count the number of have been active GradingProjectSurveys
  grading_surveys = grading_survey_logic.getForFields(fields)
  grading_survey_count = len(grading_surveys)

  for grading_survey in grading_surveys:
    if not grading_survey_logic.hasRecord(grading_survey):
      grading_survey_count = grading_survey_count - 1

  # Pre-store the needed info since Django calls the wrapper method for every
  # info call.
  info_storage = {}

  for item in list_content['data']:
    fields = {'project': item}

    # count the amount of records we have on store for this project
    project_record_count = project_logic.getQueryForFields(fields).count()
    grading_record_count = grading_logic.getQueryForFields(fields).count()

    info = {'project_surveys_total': project_survey_count,
            'project_surveys_completed': project_record_count,
            'grading_project_surveys_total': grading_survey_count,
            'grading_project_surveys_completed': grading_record_count}

    info_storage[item.key()] = info

  def wrapper(item, _):
    """Wrapper method.
    """
    return info_storage[item.key()]

  list_content['info'] = (wrapper, None)
  return list_content

def setProjectSurveyInfoForProject(list_content, project_entity,
                                   survey_params):
  """Sets the list info to a function for the given list.

  This function contains the information used for showing ProjectSurvey
  records on the StudentProject manage page.

  Args:
    list_content: list content for which to set the info
    project_entity: a StudentProject entity
    survey_params: params for the view of the type of Survey that is listed
  """

  survey_logic = survey_params['logic']
  record_logic = survey_logic.getRecordLogic()

  # store the needed info since Django calls the wrapper method for every
  # info call.
  info_storage = {}

  for survey_entity in list_content['data']:
    # try to retrieve the SurveyRecord for the given Survey and Project
    fields = {'survey': survey_entity,
              'project': project_entity}
    record_entity = record_logic.getForFields(fields, unique=True)

    info = {'record': record_entity}

    if record_entity:
      # SurveyRecord has been found store the import data in info
      info['taken_by'] = record_entity.user.name
      info['taken_on'] = record_entity.modified
      info['record_url'] = redirects.getViewSurveyRecordRedirect(record_entity,
                                                                 survey_params)
    else:
      info['taken_by'] = DEF_NO_RECORD_AVAILABLE_MESSAGE
      info['taken_on'] = DEF_NO_RECORD_AVAILABLE_MESSAGE

    take_redirect_info = {'survey': survey_entity,
                          'params': survey_params}
    info['take_url'] = redirects.getTakeProjectSurveyRedirect(
        project_entity, take_redirect_info)

    info_storage[survey_entity.key()] = info

  def wrapper(item, _):
    """Wrapper method.
    """
    return info_storage[item.key()]

  list_content['info'] = (wrapper, None)
  return list_content
<|endoftext|>


<commit_before>"""
Unit test runner.

To run:   python3 -m twest.test_all
(The twest, two, twcommon modules must be in your PYTHON_PATH.
MongoDB must be running; these tests run in (and trash) the 'testdb'
collection.)

This is a simplified version of the runner in tornado.testing.
"""

import sys
import unittest
import tornado.options
import tornado.testing

testlist = [
    'twest.test_interp',
    'twest.test_eval',
    'twest.test_funcs',
    'twest.test_propcache',
    ]

if __name__ == '__main__':

    # Sets up some logging stuff. Plus we may use the options someday.
    tornado.options.parse_command_line()

    argv = [sys.argv[0]] + testlist
    kwargs = {}
    unittest.main(module=None, argv=argv, **kwargs)
<commit_msg>Added some tests to the test_all link.<commit_after>"""
Unit test runner.

To run:   python3 -m twest.test_all
(The twest, two, twcommon modules must be in your PYTHON_PATH.
MongoDB must be running; these tests run in (and trash) the 'testdb'
collection.)

This is a simplified version of the runner in tornado.testing.
"""

import sys
import unittest
import tornado.options
import tornado.testing

testlist = [
    'twest.test_interp',
    'twest.test_eval',
    'twest.test_funcs',
    'twest.test_propcache',
    'twcommon.misc',
    'two.grammar',
    ]

if __name__ == '__main__':

    # Sets up some logging stuff. Plus we may use the options someday.
    tornado.options.parse_command_line()

    argv = [sys.argv[0]] + testlist
    kwargs = {}
    unittest.main(module=None, argv=argv, **kwargs)
<|endoftext|>


<commit_before>#!/usr/bin/env python2.5
#
# Copyright 2008 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Views for the User's own profiles.
"""

__authors__ = [
    '"Sverre Rabbelier" <sverre@rabbelier.nl>',
    '"Lennard de Rijk" <ljvderijk@gmail.com>',
    '"Pawel Solyga" <pawel.solyga@gmail.com>',
  ]

import datetime

from google.appengine.api import users
from google.appengine.ext import db

from django import forms
from django.utils import simplejson
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext

from soc.logic import cleaning
from soc.logic import dicts
from soc.logic import models as model_logic
from soc.logic.models.user import logic as user_logic
from soc.logic.models.site import logic as site_logic
from soc.views import helper
from soc.views.helper import access
from soc.views.helper import decorators
from soc.views.helper import redirects
from soc.views.helper import responses
from soc.views.helper import widgets
from soc.views.models import base
from soc.views.models import role as role_view

class View(base.View):
  """Views for User own profiles.
  """

  DEF_ROLE_LIST_MSG_FMT = ugettext("Your roles as %(name)s.")

  def __init__(self, params=None):
    """Defines the fields and methods required for the base View class
    to provide the user with list, public, create, edit and delete views.

    Params:
      params: a dict with params for this View
    """

    rights = access.Checker(params)
    rights['unspecified'] = ['deny']
    rights['any_access'] = ['allow']
    rights['create_profile'] = ['checkIsUnusedAccount']
    rights['edit_profile'] = ['checkHasUserEntity']
    rights['roles'] = ['checkIsUser']
    rights['requests'] = ['checkIsUser']
    rights['signIn'] = ['checkNotLoggedIn']
    rights['notification'] = ['checkIsUser']

    new_params = {}
    new_params['rights'] = rights
    new_params['logic'] = user_logic

    new_params['name'] = "User"
    new_params['module_name'] = "user_self"
    new_params['url_name'] = "user"

    new_params['create_template'] = 'soc/user/edit_profile.html'
    new_params['edit_template'] = 'soc/user/edit_profile.html'
    new_params['save_message'] = [ugettext('Profile saved.')]
    new_params['edit_redirect'] = '/%(url_name)s/edit_profile'

    # set the specific fields for the users profile page
    new_params['extra_dynaexclude'] = ['former_accounts',
        'account', 'is_developer', 'status', 'agreed_to_tos_on']

    new_params['create_extra_dynaproperties'] = {
        'clean_agreed_to_tos': cleaning.clean_agrees_to_tos('agreed_to_tos'),
        'clean_link_id': cleaning.clean_user_not_exist('link_id'),}

    new_params['edit_extra_dynaproperties'] = {
        'clean_link_id': cleaning.clean_user_is_current('link_id', False),
        'agreed_to_tos_on': forms.DateTimeField(
          widget=widgets.ReadOnlyInput(attrs={'disabled':'true'}),
          required=False),
        }

    new_params['sidebar_heading'] = 'User (self)'
    new_params['sidebar'] = [
        (users.create_login_url("/"), 'Sign In', 'signIn'),
        ('/' + new_params['url_name'] + '/create_profile',
            'Create Profile', 'create_profile'),
        ('/' + new_params['url_name'] + '/edit_profile',
            'Edit Profile', 'edit_profile'),
        ('/' + new_params['url_name'] + '/roles', 'Roles', 'roles'),
        ('/' + new_params['url_name'] + '/requests', 'Requests', 'requests'),
        ]

    patterns = []

    page_name = ugettext("Create your profile")
    patterns += [(r'^%(url_name)s/(?P<access_type>create_profile)$',
                  'soc.views.models.%(module_name)s.create', page_name)]

    page_name = ugettext("Edit your profile")
    patterns += [(r'^%(url_name)s/(?P<access_type>edit_profile)$',
                  'soc.views.models.%(module_name)s.edit', page_name)]

    page_name = ugettext("List of your roles")
    patterns += [(r'^%(url_name)s/(?P<access_type>roles)$',
                   'soc.views.models.user_self.roles', page_name)]

    page_name = ugettext("List of your requests")
    patterns += [(r'^%(url_name)s/(?P<access_type>requests)$',
                   'soc.views.models.request.list_self', page_name)]

    new_params['django_patterns_defaults'] = patterns

    params = dicts.merge(params, new_params)

    super(View, self).__init__(params=params)

  @decorators.merge_params
  @decorators.check_access
  def editProfile(self, request, access_type,
           page_name=None, params=None, **kwargs):
    """Displays User profile edit page for the current user.

    Args:
      request: the standard Django HTTP request object
      page_name: the page name displayed in templates as page and header title
      params: a dict with params for this View
      kwargs: The Key Fields for the specified entity
    """

    # set the link_id to the current user's link_id
    user_entity = user_logic.getForCurrentAccount()
    # pylint: disable-msg=E1103
    link_id = user_entity.link_id

    return self.edit(request, access_type, page_name=page_name,
        params=params, link_id=link_id, **kwargs)

  def editGet(self, request, entity, context, params=None):
    """Overwrite so we can add the contents of the ToS.
    For params see base.View.editGet().
    """

    s_logic = model_logic.site.logic
    site_tos = s_logic.getToS(s_logic.getSingleton())
    if site_tos:
      context['tos_contents'] = site_tos.content

    return super(View, self).editGet(request, entity, context, params=params)

  def _editGet(self, request, entity, form):
    """Sets the content of the agreed_to_tos_on field and replaces.

    Also replaces the agreed_to_tos field with a hidden field when the ToS has been signed.
    For params see base.View._editGet().
    """

    if entity.agreed_to_tos:
      form.fields['agreed_to_tos_on'].initial = entity.agreed_to_tos_on
      # replace the 'agreed_to_tos' field with a hidden field so
      # that the form checks still pass
      form.fields['agreed_to_tos'] = forms.fields.BooleanField(
          widget=forms.HiddenInput, initial=entity.agreed_to_tos, required=True)

  @decorators.mutation
  def editPost(self, request, entity, context, params=None):
    """Overwrite so we can add the contents of the ToS.
    For params see base.View.editPost().
    """

    s_logic = model_logic.site.logic
    site_tos = s_logic.getToS(s_logic.getSingleton())
    if site_tos:
      context['tos_contents'] = site_tos.content

    return super(View, self).editPost(request, entity, context, params=params)

  def _editPost(self, request, entity, fields):
    """See base.View._editPost().
    """

    # fill in the account field with the current User
    fields['account'] = users.User()

    # special actions if there is no ToS present
    s_logic = model_logic.site.logic
    site_tos = s_logic.getToS(s_logic.getSingleton())
    if not site_tos:
      # there is no Terms of Service set
      if not entity:
        # we are a new user so set the agrees_to_tos field to None
        fields['agreed_to_tos'] = None
      else:
        # editing an existing user so no value changes allowed
        fields['agreed_to_tos'] = entity.agreed_to_tos
    else:
      if not entity or not entity.agreed_to_tos:
        # set the time of agreement
        fields['agreed_to_tos_on'] = datetime.datetime.now()

    super(View, self)._editPost(request, entity, fields)

  def getRolesListData(self, request):
    """Returns the list data for roles.
    """

    user = user_logic.getForCurrentAccount()

    # only select the roles for the current user
    # pylint: disable-msg=E1103
    fields = {
        'link_id': user.link_id,
        'status': ['active', 'inactive']
        }

    keys = role_view.ROLE_VIEWS.keys()
    keys.sort()

    idx = request.GET.get('idx', '')
    idx = int(idx) if idx.isdigit() else -1

    if not 0 <= idx < len(keys):
        return responses.jsonErrorResponse(request, "idx not valid")

    idx = int(idx)
    key = keys[idx]
    list_params = role_view.ROLE_VIEWS[key].getParams()

    contents = helper.lists.getListData(request, list_params, fields)

    json = simplejson.dumps(contents)
    return responses.jsonResponse(request, json)

  @decorators.merge_params
  @decorators.check_access
  def roles(self, request, access_type,
               page_name=None, params=None, **kwargs):
    """Displays the valid roles for this user.

    Args:
      request: the standard Django HTTP request object
      access_type : the name of the access type which should be checked
      page_name: the page name displayed in templates as page and header title
      params: a dict with params for this View
      kwargs: not used
    """

    if request.GET.get('fmt') == 'json':
      return self.getRolesListData(request)

    contents = []

    site = site_logic.getSingleton()
    site_name = site.site_name

    params = params.copy()

    i = 0
    for _, loop_view in sorted(role_view.ROLE_VIEWS.iteritems()):
      if loop_view.getParams()['show_in_roles_overview']:
        list_params = loop_view.getParams().copy()
        list_params['list_description'] = self.DEF_ROLE_LIST_MSG_FMT % list_params
        list = helper.lists.getListGenerator(request, list_params, idx=i)
        contents.append(list)
        i += 1

    return self._list(request, params, contents, page_name)

  def getSidebarMenus(self, id, user, params=None):
    """See base.View.getSidebarMenus().
    """

    link_title = ugettext('Notifications')

    filter = {
        'scope': user,
        'unread': True,
        }

    notifications = model_logic.notification.logic.getForFields(filter)
    count = len(list(notifications))

    if count > 0:
      link_title = '%s (%d)' % (
          force_unicode(link_title), count)
      link_title = mark_safe(link_title)

    items = [('/' + 'notification/list', link_title, 'notification')]
    if user:
      items += [(redirects.getCreateDocumentRedirect(user, 'user'),
          "Create a New Document", 'any_access')]

      items += [(redirects.getListDocumentsRedirect(user, 'user'),
          "List Documents", 'any_access')]

    new_params = {}
    new_params['sidebar_additional'] = items

    params = dicts.merge(params, new_params)

    return super(View, self).getSidebarMenus(id, user, params=params)

view = View()

create = decorators.view(view.create)
edit = decorators.view(view.editProfile)
export = decorators.view(view.export)
roles = decorators.view(view.roles)
<commit_msg>Fixed roles showing up under the wrong list in user_self.<commit_after>#!/usr/bin/env python2.5
#
# Copyright 2008 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Views for the User's own profiles.
"""

__authors__ = [
    '"Sverre Rabbelier" <sverre@rabbelier.nl>',
    '"Lennard de Rijk" <ljvderijk@gmail.com>',
    '"Pawel Solyga" <pawel.solyga@gmail.com>',
  ]

import datetime

from google.appengine.api import users
from google.appengine.ext import db

from django import forms
from django.utils import simplejson
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext

from soc.logic import cleaning
from soc.logic import dicts
from soc.logic import models as model_logic
from soc.logic.models.user import logic as user_logic
from soc.logic.models.site import logic as site_logic
from soc.views import helper
from soc.views.helper import access
from soc.views.helper import decorators
from soc.views.helper import redirects
from soc.views.helper import responses
from soc.views.helper import widgets
from soc.views.models import base
from soc.views.models import role as role_view

class View(base.View):
  """Views for User own profiles.
  """

  DEF_ROLE_LIST_MSG_FMT = ugettext("Your roles as %(name)s.")

  def __init__(self, params=None):
    """Defines the fields and methods required for the base View class
    to provide the user with list, public, create, edit and delete views.

    Params:
      params: a dict with params for this View
    """

    rights = access.Checker(params)
    rights['unspecified'] = ['deny']
    rights['any_access'] = ['allow']
    rights['create_profile'] = ['checkIsUnusedAccount']
    rights['edit_profile'] = ['checkHasUserEntity']
    rights['roles'] = ['checkIsUser']
    rights['requests'] = ['checkIsUser']
    rights['signIn'] = ['checkNotLoggedIn']
    rights['notification'] = ['checkIsUser']

    new_params = {}
    new_params['rights'] = rights
    new_params['logic'] = user_logic

    new_params['name'] = "User"
    new_params['module_name'] = "user_self"
    new_params['url_name'] = "user"

    new_params['create_template'] = 'soc/user/edit_profile.html'
    new_params['edit_template'] = 'soc/user/edit_profile.html'
    new_params['save_message'] = [ugettext('Profile saved.')]
    new_params['edit_redirect'] = '/%(url_name)s/edit_profile'

    # set the specific fields for the users profile page
    new_params['extra_dynaexclude'] = ['former_accounts',
        'account', 'is_developer', 'status', 'agreed_to_tos_on']

    new_params['create_extra_dynaproperties'] = {
        'clean_agreed_to_tos': cleaning.clean_agrees_to_tos('agreed_to_tos'),
        'clean_link_id': cleaning.clean_user_not_exist('link_id'),}

    new_params['edit_extra_dynaproperties'] = {
        'clean_link_id': cleaning.clean_user_is_current('link_id', False),
        'agreed_to_tos_on': forms.DateTimeField(
          widget=widgets.ReadOnlyInput(attrs={'disabled':'true'}),
          required=False),
        }

    new_params['sidebar_heading'] = 'User (self)'
    new_params['sidebar'] = [
        (users.create_login_url("/"), 'Sign In', 'signIn'),
        ('/' + new_params['url_name'] + '/create_profile',
            'Create Profile', 'create_profile'),
        ('/' + new_params['url_name'] + '/edit_profile',
            'Edit Profile', 'edit_profile'),
        ('/' + new_params['url_name'] + '/roles', 'Roles', 'roles'),
        ('/' + new_params['url_name'] + '/requests', 'Requests', 'requests'),
        ]

    patterns = []

    page_name = ugettext("Create your profile")
    patterns += [(r'^%(url_name)s/(?P<access_type>create_profile)$',
                  'soc.views.models.%(module_name)s.create', page_name)]

    page_name = ugettext("Edit your profile")
    patterns += [(r'^%(url_name)s/(?P<access_type>edit_profile)$',
                  'soc.views.models.%(module_name)s.edit', page_name)]

    page_name = ugettext("List of your roles")
    patterns += [(r'^%(url_name)s/(?P<access_type>roles)$',
                   'soc.views.models.user_self.roles', page_name)]

    page_name = ugettext("List of your requests")
    patterns += [(r'^%(url_name)s/(?P<access_type>requests)$',
                   'soc.views.models.request.list_self', page_name)]

    new_params['django_patterns_defaults'] = patterns

    params = dicts.merge(params, new_params)

    super(View, self).__init__(params=params)

  @decorators.merge_params
  @decorators.check_access
  def editProfile(self, request, access_type,
           page_name=None, params=None, **kwargs):
    """Displays User profile edit page for the current user.

    Args:
      request: the standard Django HTTP request object
      page_name: the page name displayed in templates as page and header title
      params: a dict with params for this View
      kwargs: The Key Fields for the specified entity
    """

    # set the link_id to the current user's link_id
    user_entity = user_logic.getForCurrentAccount()
    # pylint: disable-msg=E1103
    link_id = user_entity.link_id

    return self.edit(request, access_type, page_name=page_name,
        params=params, link_id=link_id, **kwargs)

  def editGet(self, request, entity, context, params=None):
    """Overwrite so we can add the contents of the ToS.
    For params see base.View.editGet().
    """

    s_logic = model_logic.site.logic
    site_tos = s_logic.getToS(s_logic.getSingleton())
    if site_tos:
      context['tos_contents'] = site_tos.content

    return super(View, self).editGet(request, entity, context, params=params)

  def _editGet(self, request, entity, form):
    """Sets the content of the agreed_to_tos_on field and replaces.

    Also replaces the agreed_to_tos field with a hidden field when the ToS has been signed.
    For params see base.View._editGet().
    """

    if entity.agreed_to_tos:
      form.fields['agreed_to_tos_on'].initial = entity.agreed_to_tos_on
      # replace the 'agreed_to_tos' field with a hidden field so
      # that the form checks still pass
      form.fields['agreed_to_tos'] = forms.fields.BooleanField(
          widget=forms.HiddenInput, initial=entity.agreed_to_tos, required=True)

  @decorators.mutation
  def editPost(self, request, entity, context, params=None):
    """Overwrite so we can add the contents of the ToS.
    For params see base.View.editPost().
    """

    s_logic = model_logic.site.logic
    site_tos = s_logic.getToS(s_logic.getSingleton())
    if site_tos:
      context['tos_contents'] = site_tos.content

    return super(View, self).editPost(request, entity, context, params=params)

  def _editPost(self, request, entity, fields):
    """See base.View._editPost().
    """

    # fill in the account field with the current User
    fields['account'] = users.User()

    # special actions if there is no ToS present
    s_logic = model_logic.site.logic
    site_tos = s_logic.getToS(s_logic.getSingleton())
    if not site_tos:
      # there is no Terms of Service set
      if not entity:
        # we are a new user so set the agrees_to_tos field to None
        fields['agreed_to_tos'] = None
      else:
        # editing an existing user so no value changes allowed
        fields['agreed_to_tos'] = entity.agreed_to_tos
    else:
      if not entity or not entity.agreed_to_tos:
        # set the time of agreement
        fields['agreed_to_tos_on'] = datetime.datetime.now()

    super(View, self)._editPost(request, entity, fields)

  def getRolesListData(self, request):
    """Returns the list data for roles.
    """

    user = user_logic.getForCurrentAccount()

    # only select the roles for the current user
    # pylint: disable-msg=E1103
    fields = {
        'link_id': user.link_id,
        'status': ['active', 'inactive']
        }

    keys = role_view.ROLE_VIEWS.keys()
    keys.sort()

    idx = request.GET.get('idx', '')
    idx = int(idx) if idx.isdigit() else -1

    if not 0 <= idx < len(keys):
        return responses.jsonErrorResponse(request, "idx not valid")

    idx = int(idx)
    key = keys[idx]
    list_params = role_view.ROLE_VIEWS[key].getParams()

    contents = helper.lists.getListData(request, list_params, fields)

    json = simplejson.dumps(contents)
    return responses.jsonResponse(request, json)

  @decorators.merge_params
  @decorators.check_access
  def roles(self, request, access_type,
               page_name=None, params=None, **kwargs):
    """Displays the valid roles for this user.

    Args:
      request: the standard Django HTTP request object
      access_type : the name of the access type which should be checked
      page_name: the page name displayed in templates as page and header title
      params: a dict with params for this View
      kwargs: not used
    """

    if request.GET.get('fmt') == 'json':
      return self.getRolesListData(request)

    contents = []

    site = site_logic.getSingleton()
    site_name = site.site_name

    params = params.copy()

    i = 0
    for _, loop_view in sorted(role_view.ROLE_VIEWS.iteritems()):
      if loop_view.getParams()['show_in_roles_overview']:
        list_params = loop_view.getParams().copy()
        list_params['list_description'] = self.DEF_ROLE_LIST_MSG_FMT % list_params
        list = helper.lists.getListGenerator(request, list_params, idx=i)
        contents.append(list)
      # keep on counting because index is important
      i += 1

    return self._list(request, params, contents, page_name)

  def getSidebarMenus(self, id, user, params=None):
    """See base.View.getSidebarMenus().
    """

    link_title = ugettext('Notifications')

    filter = {
        'scope': user,
        'unread': True,
        }

    notifications = model_logic.notification.logic.getForFields(filter)
    count = len(list(notifications))

    if count > 0:
      link_title = '%s (%d)' % (
          force_unicode(link_title), count)
      link_title = mark_safe(link_title)

    items = [('/' + 'notification/list', link_title, 'notification')]
    if user:
      items += [(redirects.getCreateDocumentRedirect(user, 'user'),
          "Create a New Document", 'any_access')]

      items += [(redirects.getListDocumentsRedirect(user, 'user'),
          "List Documents", 'any_access')]

    new_params = {}
    new_params['sidebar_additional'] = items

    params = dicts.merge(params, new_params)

    return super(View, self).getSidebarMenus(id, user, params=params)

view = View()

create = decorators.view(view.create)
edit = decorators.view(view.editProfile)
export = decorators.view(view.export)
roles = decorators.view(view.roles)
<|endoftext|>


<commit_before>'''
Add the Amazing feature to co-write fictional or non-fictional epics.
'''
import os
from datetime import datetime

import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from stickord.registry import Command, get_easy_logger, role_whitelist

Base = declarative_base() # pylint: disable=invalid-name
LOGGER = get_easy_logger('commands.storywriting')

class StoryElement(Base):
    ''' Represents a story piece of 3 words. '''
    __tablename__ = 'stories'

    id = sa.Column(sa.Integer, primary_key=True) # pylint: disable=invalid-name
    story_string = sa.Column(sa.String, nullable=False)
    author = sa.Column(sa.String, nullable=False)
    story_id = sa.Column(sa.Integer, nullable=False)
    created_at = sa.Column(sa.DateTime, default=datetime.utcnow)

    def __repr__(self):
        ''' Represent as <StoryElement(id, story_string, author,
        story_id, created_at)>.'''
        return (f'<StoryElement({self.id}, {self.story_string}, {self.author},'
                f' {self.story_id}, {self.created_at})>')

@Command(['addstory', 'as'], category='Games')
async def add_to_story(cont, mesg, client, sessionmaker, *_args, **_kwargs):
    ''' Adds the (first) three words to the currently active story. '''
    session = sessionmaker()
    curr_story = session.query(StoryElement)\
        .order_by(StoryElement.created_at.desc()).first()

    if not curr_story:
        LOGGER.info('No story yet, setting id to 0')
        curr_story_id = 0
    else:
        curr_story_id = curr_story.story_id

    if len(cont) < 3:
        LOGGER.info(
            '%s wanted to add to %s but did not submit 3 words',
            mesg.author.mention, curr_story_id
        )
        response = (f'Oh! Something went wrong, '
                    f'I could not find 3 words to add to the story')
    elif mesg.author.id == curr_story.author:
        response = (f'You can\'t submit words twice in a row!\n'
                    f'What anarchy that would be..')
    else:
        story_str = ' '.join(cont[:3])
        add_story_element(session, story_str, mesg.author, curr_story_id)
        await client.add_reaction(mesg, '\U0001f4be')
        response = None

    session.commit()
    return response

@Command(['currentstory', 'printstory', 'cs'], category='Games')
async def print_current_story(*args, **_kwargs):
    ''' Print the story that the creative masterminds are currently writing. '''
    session = args[3]()
    curr_story = session.query(StoryElement)\
        .order_by(StoryElement.created_at.desc()).first()

    if not curr_story:
        return 'No story yet'
    else:
        curr_story_id = curr_story.story_id

    return format_story(get_story(session, curr_story_id))

@Command(['endstory', 'finishstory'], category='Games')
@role_whitelist(['Admin', 'Moderator'])
async def end_current_story(cont, mesg, client, sessionmaker, *_args, **_kwargs):
    ''' Ends the current story and save it under the name specified (Moderator only). '''
    session = sessionmaker()

    curr_story = session.query(StoryElement)\
        .order_by(StoryElement.created_at.desc()).first()

    if not curr_story:
        LOGGER.info(
            '%s tried to save a story but no story elements were found',
            mesg.author.mention
        )
        return 'No story to save.'

    current_story_id = curr_story.story_id

    story = get_story(session, current_story_id)
    story = format_story(story)
    storyname = ''.join(cont)

    if not storyname:
        return 'You can\'t save a story without a name!'

    save_story(story, storyname)

    add_story_element(session, '', mesg.author, current_story_id + 1)
    session.commit()

    storypost = f'{storyname}\n{story}'
    await post_story(storypost, mesg.server, client)
    return f'Story saved as \'{storyname}\'.'

@Command(['openstory', 'loadstory'], hidden=True)
@role_whitelist(['Admin', 'Moderator'])
async def load_story(cont, *_args, **_kwargs):
    ''' Loads a previously ended story by name (Moderator only). '''
    storyname = ''.join(cont)
    if not storyname:
        return 'You have to enter a story name'

    return print_story(storyname)

@Command(['storyundo', 'undostory'], hidden=True)
@role_whitelist(['Admin', 'Moderator'])
async def undo_story(cont, mesg, client, sessionmaker, *_args, **_kwargs):
    ''' Deltetes the last x messages from the database (Moderator only).
    WARNING: Does not take in to account story endings etc, use with discretion. '''
    session = sessionmaker()

    if cont:
        try:
            x = int(cont[0])
        except ValueError:
            return 'Could not parse value, did nothing.'
    else:
        x = 1

    curr_selection = session.query(StoryElement)\
        .order_by(StoryElement.created_at.desc())[:x]

    for entry in curr_selection:
        session.delete(entry)

    await client.add_reaction(mesg, '\U000023ea')
    session.commit()

    return None

def save_story(story, name):
    ''' Saves a story to a txt file in the Stories folder. '''
    fname = f'{name.lower()}.txt'
    fpath = os.path.join('Stories', fname)

    with open(fpath, 'a') as file:
        file.write(story)

    return None

def print_story(name):
    ''' Get's a saved story from file and returns the string. '''
    fname = f'{name.lower()}.txt'
    fpath = os.path.join('Stories', fname)

    if not os.path.isfile(fpath):
        return 'This story doesn\'t exist.'

    with open(fpath, 'r') as file:
        string = file.read()

    return string

def format_story(string):
    ''' Formats a string text to include endlines after "." and strip whitespaces from beginning and end. '''
    string = string.strip(' \t\n\r')
    string = string.replace('. ', '.\n')
    return string

def get_story(session, story_id):
    ''' Returns a string containing the story with a specific id. '''
    story_segs = session.query(StoryElement).filter(StoryElement.story_id == story_id) \
        .order_by(StoryElement.created_at.asc())

    story = ' '.join(s[0] for s in story_segs.values('story_string'))
    return story

def add_story_element(session, string, author, story):
    ''' Add a database entry for the new story. '''
    LOGGER.debug(
        'Added "%s" to the story %s, committed by %s',
        string, story, author.id
    )

    author_id = author.id
    session.add(StoryElement(
        story_string=string,
        author=author_id,
        story_id=story
    ))

async def post_story(story, server, client):
    ''' Have the Client post the bot to the "stories" channel if it exists. '''
    channel_list = server.channels
    for channel in channel_list:
        if channel.name.lower() == "stories":
            response_channel = channel

    if not channel:
        return
    return await client.send_message(response_channel, story)

<commit_msg>Added channel whitelist for storywriting<commit_after>'''
Add the Amazing feature to co-write fictional or non-fictional epics.
'''
import os
from datetime import datetime

import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from stickord.registry import Command, get_easy_logger, role_whitelist, channel_whitelist

Base = declarative_base() # pylint: disable=invalid-name
LOGGER = get_easy_logger('commands.storywriting')

class StoryElement(Base):
    ''' Represents a story piece of 3 words. '''
    __tablename__ = 'stories'

    id = sa.Column(sa.Integer, primary_key=True) # pylint: disable=invalid-name
    story_string = sa.Column(sa.String, nullable=False)
    author = sa.Column(sa.String, nullable=False)
    story_id = sa.Column(sa.Integer, nullable=False)
    created_at = sa.Column(sa.DateTime, default=datetime.utcnow)

    def __repr__(self):
        ''' Represent as <StoryElement(id, story_string, author,
        story_id, created_at)>.'''
        return (f'<StoryElement({self.id}, {self.story_string}, {self.author},'
                f' {self.story_id}, {self.created_at})>')

@Command(['addstory', 'as'], category='Games')
@channel_whitelist(['storywriting'])
async def add_to_story(cont, mesg, client, sessionmaker, *_args, **_kwargs):
    ''' Adds the (first) three words to the currently active story. '''
    session = sessionmaker()
    curr_story = session.query(StoryElement)\
        .order_by(StoryElement.created_at.desc()).first()

    if not curr_story:
        LOGGER.info('No story yet, setting id to 0')
        curr_story_id = 0
    else:
        curr_story_id = curr_story.story_id

    if len(cont) < 3:
        LOGGER.info(
            '%s wanted to add to %s but did not submit 3 words',
            mesg.author.mention, curr_story_id
        )
        response = (f'Oh! Something went wrong, '
                    f'I could not find 3 words to add to the story')
    elif mesg.author.id == curr_story.author:
        response = (f'You can\'t submit words twice in a row!\n'
                    f'What anarchy that would be..')
    else:
        story_str = ' '.join(cont[:3])
        add_story_element(session, story_str, mesg.author, curr_story_id)
        await client.add_reaction(mesg, '\U0001f4be')
        response = None

    session.commit()
    return response

@Command(['currentstory', 'printstory', 'cs'], category='Games')
@channel_whitelist(['storywriting'])
async def print_current_story(*args, **_kwargs):
    ''' Print the story that the creative masterminds are currently writing. '''
    session = args[3]()
    curr_story = session.query(StoryElement)\
        .order_by(StoryElement.created_at.desc()).first()

    if not curr_story:
        return 'No story yet'
    else:
        curr_story_id = curr_story.story_id

    return format_story(get_story(session, curr_story_id))

@Command(['endstory', 'finishstory'], category='Games')
@role_whitelist(['Admin', 'Moderator'])
async def end_current_story(cont, mesg, client, sessionmaker, *_args, **_kwargs):
    ''' Ends the current story and save it under the name specified (Moderator only). '''
    session = sessionmaker()

    curr_story = session.query(StoryElement)\
        .order_by(StoryElement.created_at.desc()).first()

    if not curr_story:
        LOGGER.info(
            '%s tried to save a story but no story elements were found',
            mesg.author.mention
        )
        return 'No story to save.'

    current_story_id = curr_story.story_id

    story = get_story(session, current_story_id)
    story = format_story(story)
    storyname = ''.join(cont)

    if not storyname:
        return 'You can\'t save a story without a name!'

    save_story(story, storyname)

    add_story_element(session, '', mesg.author, current_story_id + 1)
    session.commit()

    storypost = f'{storyname}\n{story}'
    await post_story(storypost, mesg.server, client)
    return f'Story saved as \'{storyname}\'.'

@Command(['openstory', 'loadstory'], hidden=True)
@role_whitelist(['Admin', 'Moderator'])
async def load_story(cont, *_args, **_kwargs):
    ''' Loads a previously ended story by name (Moderator only). '''
    storyname = ''.join(cont)
    if not storyname:
        return 'You have to enter a story name'

    return print_story(storyname)

@Command(['storyundo', 'undostory'], hidden=True)
@role_whitelist(['Admin', 'Moderator'])
async def undo_story(cont, mesg, client, sessionmaker, *_args, **_kwargs):
    ''' Deltetes the last x messages from the database (Moderator only).
    WARNING: Does not take in to account story endings etc, use with discretion. '''
    session = sessionmaker()

    if cont:
        try:
            x = int(cont[0])
        except ValueError:
            return 'Could not parse value, did nothing.'
    else:
        x = 1

    curr_selection = session.query(StoryElement)\
        .order_by(StoryElement.created_at.desc())[:x]

    for entry in curr_selection:
        session.delete(entry)

    await client.add_reaction(mesg, '\U000023ea')
    session.commit()

    return None

def save_story(story, name):
    ''' Saves a story to a txt file in the Stories folder. '''
    fname = f'{name.lower()}.txt'
    fpath = os.path.join('Stories', fname)

    with open(fpath, 'a') as file:
        file.write(story)

    return None

def print_story(name):
    ''' Get's a saved story from file and returns the string. '''
    fname = f'{name.lower()}.txt'
    fpath = os.path.join('Stories', fname)

    if not os.path.isfile(fpath):
        return 'This story doesn\'t exist.'

    with open(fpath, 'r') as file:
        string = file.read()

    return string

def format_story(string):
    ''' Formats a string text to include endlines after "." and strip whitespaces from beginning and end. '''
    string = string.strip(' \t\n\r')
    string = string.replace('. ', '.\n')
    return string

def get_story(session, story_id):
    ''' Returns a string containing the story with a specific id. '''
    story_segs = session.query(StoryElement).filter(StoryElement.story_id == story_id) \
        .order_by(StoryElement.created_at.asc())

    story = ' '.join(s[0] for s in story_segs.values('story_string'))
    return story

def add_story_element(session, string, author, story):
    ''' Add a database entry for the new story. '''
    LOGGER.debug(
        'Added "%s" to the story %s, committed by %s',
        string, story, author.id
    )

    author_id = author.id
    session.add(StoryElement(
        story_string=string,
        author=author_id,
        story_id=story
    ))

async def post_story(story, server, client):
    ''' Have the Client post the bot to the "stories" channel if it exists. '''
    channel_list = server.channels
    for channel in channel_list:
        if channel.name.lower() == "stories":
            response_channel = channel

    if not channel:
        return
    return await client.send_message(response_channel, story)

<|endoftext|>