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'
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'
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
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
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'
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]
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ázquez Acosta") # doctest: +SKIP 'manuel-vazquez-acosta' If `entities` is False, then no HTML-entities substitution is made:: >>> value = "Manuel Vá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ázquez Acosta') # doctest: +SKIP 'manuel-vazquez-acosta' >>> value = 'Manuel Vá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ázquez Acosta') # doctest: +SKIP 'manuel-vazquez-acosta' >>> slugify('Manuel Vá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, '-')
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)