예제 #1
0
def test_normalize_slug_hypothesis(s, invalids, replacement):
    from xoutil.string import normalize_slug

    assert ' ' not in normalize_slug(s), \
        'Slugs do not contain spaces'

    assert ' ' in normalize_slug(s + ' ', valids=' '), \
        'Slugs do contain spaces if explicitly allowed'

    assert all(c not in normalize_slug(s, replacement, invalids=c)
               for c in invalids if c not in replacement), \
        'Slugs dont contain invalid chars'
예제 #2
0
def test_normalize_slug_hypothesis(s, invalids):
    from xoutil.string import normalize_slug

    assert ' ' not in normalize_slug(s), \
        'Slugs do not contain spaces'

    assert ' ' in normalize_slug(s + ' ', valids=' '), \
        'Slugs do contain spaces if explicitly allowed'

    # TODO (med): The following fails with s='0' and invalids='0'.  Is this a
    # true invariant?
    assert all(c not in normalize_slug(s) for c in invalids), \
        'Slugs dont contain invalid chars'
예제 #3
0
 def create(self, values):
     """Odoo create by default a mail alias.This function allows you to
        create more than one mail alias since a project is created for the
        first time.
     """
     from xoutil.string import normalize_slug
     aliases_create = []
     if not values.get('alias_name'):
         values['alias_name'] = normalize_slug(values['name'])
         Alias = self.env['mail.alias']
         vals = {}
         valias = values.pop('alias_ids', None)
         if valias:
             use_lead = values.get('use_leads', None)
             use_opportunitie = values.get('use_opportunities', None)
             aliases_create = self.set_values(valias, use_lead, use_opportunitie)
     sale_team = super(crm_case_section, self).create(values)
     if sale_team and aliases_create:
         for alias in aliases_create:
             record = Alias.browse(alias)
             defaults = eval(record.alias_defaults)
             if MAJOR_ODOO_VERSION < 9:
                 defaults.update(section_id=sale_team.id)
             else:
                 # Odoo 9 and 10 changed the section_id to a team_id.
                 defaults.update(team_id=sale_team.id)
             vals['alias_defaults'] = repr(defaults)
             record.write(vals)
     return sale_team
예제 #4
0
 def create(self, values):
     """Odoo create by deafault a mail alias.This function allows you to
        create more than one mail alias since a project is created for the
        first time.
     """
     from xoutil.string import normalize_slug
     if not values.get('alias_name'):
         values['alias_name'] = normalize_slug(values['name'])
     if MAJOR_ODOO_VERSION < 10:
         Alias = self.env['mail.alias']
         vals = {}
         if 'alias_ids' in values:
             valias = values.pop('alias_ids')
             use_issue = values.get('use_issues', None)
             use_task = values.get('use_tasks', None)
             aliases_create = self.set_values(valias, use_issue, use_task)
         else:
             aliases_create = []
         project = super(project_project, self).create(values)
         if project and aliases_create:
             for alias in aliases_create:
                 record = Alias.browse(alias)
                 values_default = eval(record.alias_defaults)
                 values_default.update(project_id=project.id,
                                       user_id=project.user_id.id)
                 vals['alias_defaults'] = repr(values_default)
                 record.write(vals)
     if MAJOR_ODOO_VERSION > 9:
         project = super(project_project, self).create(values)
     return project
예제 #5
0
def test_normalize_slug():
    from xoutil.string import normalize_slug
    assert normalize_slug('  Á.e i  Ó  u  ') == 'a-e-i-o-u'
    assert normalize_slug('  Á.e i  Ó  u  ', '.', invalids='AU') == 'e.i.o'
    assert normalize_slug('  Á.e i  Ó  u  ', valids='.') == 'a.e-i-o-u'
    assert normalize_slug('_x', '_') == '_x'
    assert normalize_slug('-x', '_') == 'x'
    assert normalize_slug('-x-y-', '_') == 'x_y'
    assert normalize_slug(None) == 'none'
    assert normalize_slug(1 == 1) == 'true'
    assert normalize_slug(1.0) == '1-0'
    assert normalize_slug(135) == '135'
    assert normalize_slug(123456, '', invalids='52') == '1346'
    assert normalize_slug('_x', '_') == '_x'
예제 #6
0
def generate_password(pass_phrase, level=DEFAULT_PASS_PHRASE_LEVEL):
    """Generate a password from a source `pass-phrase` and a security `level`.

    :param pass_phrase: String pass-phrase to be used as base of password
                        generation process.

    :param level: Numerical security level (the bigger the more secure, but
                  don't exaggerate!).

    When `pass_phrase` is a valid string, `level` means a generation method.
    Each level implies all other with an inferior numerical value.

    There are several definitions with numerical values for `level` (0-4):

    :data:`PASS_PHRASE_LEVEL_BASIC`

        Generate the same pass-phrase, just removing invalid characters and
        converting the result to lower-case.

    :data:`PASS_PHRASE_LEVEL_MAPPED`

        Replace some characters with new values: ``'e'->'3'``, ``'i'->'1'``,
        ``'o'->'0'``, ``'s'->'5'``.

    :data:`PASS_PHRASE_LEVEL_MAPPED_MIXED`

        Consonants characters before 'M' (included) are converted to
        upper-case, all other are kept lower-case.

    :data:`PASS_PHRASE_LEVEL_MAPPED_DATED`

        Adds a suffix with the year of current date ("<YYYY>").

    :data:`PASS_PHRASE_LEVEL_STRICT`

        Randomly scramble previous result until unbreakable strong password is
        obtained.

    If `pass_phrase` is ``None`` or an empty string, generate a "secure salt"
    (a password not based in a source pass-phrase).  A "secure salt" is
    generated by scrambling the concatenation of a random phrases from the
    alphanumeric vocabulary.

    Returned password size is ``4*level`` except when a `pass-phrase` is given
    for `level` <= 4 where depend on the count of valid characters of
    `pass-phrase` argument, although minimum required is warranted.  When
    `pass-phrase` is ``None`` for `level` zero or negative, size ``4`` is
    assumed.  First four levels are considered weak.

    Maximum size is defined in the :data:`MAX_PASSWORD_SIZE` constant.

    Default level is :data:`PASS_PHRASE_LEVEL_MAPPED_DATED` when using a
    pass-phrase.

    """
    from random import sample, randint
    from xoutil.string import normalize_slug

    level = _normalize_level(level)
    size = MAX_PASSWORD_SIZE + 1  # means, return all calculated
    required = min(max(level, 1) * BASIC_PASSWORD_SIZE, MAX_PASSWORD_SIZE)
    if pass_phrase:
        # PASS_PHRASE_LEVEL_BASIC
        res = normalize_slug(pass_phrase, "", invalids="_")
        if level >= PASS_PHRASE_LEVEL_MAPPED:
            for (old, new) in ("e3", "i1", "o0", "s5"):
                res = res.replace(old, new)
        if level >= PASS_PHRASE_LEVEL_MAPPED_MIXED:
            for new in "BCDFGHJKLM":
                old = new.lower()
                res = res.replace(old, new)
        if level >= PASS_PHRASE_LEVEL_MAPPED_DATED:
            from datetime import datetime

            today = datetime.today()
            res += today.strftime("%Y")
        if level >= PASS_PHRASE_LEVEL_STRICT:
            size = required
    else:
        size = required
        count = randint(BASIC_PASSWORD_SIZE, 2 * BASIC_PASSWORD_SIZE)
        res = "".join(sample(SAMPLE, count))
    if size <= MAX_PASSWORD_SIZE:
        if len(res) < size:
            needed = (size - len(res)) // BASIC_PASSWORD_SIZE + 1
            res += generate_password(None, needed)
        res = "".join(sample(res, size))
    return res[:size]
예제 #7
0
def slugify(s, entities=True, decimal=True, hexadecimal=True):
    '''
    Normalizes string, converts to lower-case, removes non-alpha characters,
    and converts spaces to hyphens.

    Parts from http://www.djangosnippets.org/snippets/369/

        >>> slugify("Manuel Vázquez Acosta")    # doctest: +SKIP
        'manuel-vazquez-acosta'

    If `s` and `entities` is True (the default) all HTML entities
    are replaced by its equivalent character before normalization::

        >>> slugify("Manuel V&aacute;zquez Acosta")   # doctest: +SKIP
        'manuel-vazquez-acosta'

    If `entities` is False, then no HTML-entities substitution is made::

        >>> value = "Manuel V&aacute;zquez Acosta"
        >>> slugify(value, entities=False)  # doctest: +SKIP
        'manuel-v-aacute-zquez-acosta'

    If `decimal` is True, then all entities of the form ``&#nnnn`` where
    `nnnn` is a decimal number deemed as a unicode codepoint, are replaced by
    the corresponding unicode character::

        >>> slugify('Manuel V&#225;zquez Acosta')  # doctest: +SKIP
        'manuel-vazquez-acosta'

        >>> value = 'Manuel V&#225;zquez Acosta'
        >>> slugify(value, decimal=False)  # doctest: +SKIP
        'manuel-v-225-zquez-acosta'


    If `hexadecimal` is True, then all entities of the form ``&#nnnn`` where
    `nnnn` is a hexdecimal number deemed as a unicode codepoint, are replaced
    by the corresponding unicode character::

        >>> slugify('Manuel V&#x00e1;zquez Acosta')  # doctest: +SKIP
        'manuel-vazquez-acosta'

        >>> slugify('Manuel V&#x00e1;zquez Acosta', hexadecimal=False)  # doctest: +SKIP
        'manuel-v-x00e1-zquez-acosta'

    '''
    import re
    from xoutil.eight import unichr, text_type
    from xoutil.string import safe_decode, normalize_slug
    if not isinstance(s, text_type):
        s = safe_decode(s)
    if entities:
        try:
            from htmlentitydefs import name2codepoint
        except ImportError:
            # Py3k: The ``htmlentitydefs`` module has been renamed to
            # ``html.entities`` in Python 3
            from html.entities import name2codepoint
        s = re.sub(str('&(%s);') % str('|').join(name2codepoint),
                   lambda m: unichr(name2codepoint[m.group(1)]), s)
    if decimal:
        try:
            s = re.sub(r'&#(\d+);', lambda m: unichr(int(m.group(1))), s)
        except:
            pass
    if hexadecimal:
        try:
            s = re.sub(r'&#x([\da-fA-F]+);',
                       lambda m: unichr(int(m.group(1), 16)), s)
        except:
            pass
    return normalize_slug(s, '-')
예제 #8
0
def _get_best_name(names, safe=False, full=False):
    '''Get the best name in the give list of `names`.

    Best names are chosen in the following order (from worst to best):

    - Any string
    - A valid slug
    - A valid protected identifier
    - A valid public identifier
    - A valid full identifier

    If a string in the list `names` contains the substring "%(next)s", then
    the algorithm recurses to find the best name of the remaining names first
    and substitutes the substring with the result, the remaining names are
    then pruned from the search.

    If `safe` is True, returned name must be a valid identifier.  If `full` is
    True (halted if `safe` is not True) then the returned name must a valid
    full identifier.

    '''
    from xoutil.validators import (is_valid_full_identifier,
                                   is_valid_public_identifier,
                                   is_valid_identifier,
                                   is_valid_slug)
    names = list(names)

    def inner(start=0):
        ok, best_idx, best_qlty = start, -1, 0
        i, count = start, len(names)
        assert start < count, 'start is "%s", max is "%s".' % (start, count)
        while i < count:
            name = names[i]
            if '%(next)s' in name:
                next = inner(i + 1)
                names[i] = name % {'next': next}
                count = i + 1
            else:
                if is_valid_slug(name):
                    qlty = 25
                if is_valid_identifier(name):
                    qlty = 75 if is_valid_public_identifier(name) else 50
                elif is_valid_full_identifier(name):
                    qlty = 100
                else:
                    qlty = -25
                if best_qlty <= qlty:
                    best_idx = i
                    best_qlty = qlty
                ok = i
                i += 1
        idx = best_idx if best_idx >= 0 else ok
        return names[idx]
    res = inner()
    if safe:
        # TODO: Improve these methods to return False of reserved identifiers
        is_valid = is_valid_full_identifier if full else is_valid_identifier
        if not is_valid(res):
            from xoutil.string import normalize_slug
            _mark = 'dot_dot_dot'
            full = full and '.' in res
            if full:
                res = res.replace('.', _mark)
            res = normalize_slug(res, '_')
            if full:
                res = res.replace(_mark, '.')
            if not is_valid(res):
                res = '_' + res
    return str(res)