"""Generate a network device's asset number from its type prefix and name. Every network device on this fleet already follows one convention, applied by hand: AP-, SW-, SVR-, IDF-. This turns that into something the create path does, so the same value is not typed twice. Generation only fills a BLANK asset number. A device that carries a real identifier of its own - a vendor tag, a controller name, a serial - keeps it. That is the platform rule: adopt an external identifier where one exists, derive one only where none does. """ import re # Characters that must not reach a unique business key. assetnumber ends up in # URLs and in joins other subsystems make, and the existing data already shows # why this matters: IDF-Telco-Demarc-#1 came from the name 'Telco Demarc #1', # carrying a '#' into an identifier. _SEPARATORS = re.compile(r'[\s_/\\]+') _ILLEGAL = re.compile(r'[^A-Za-z0-9.-]') _RUNS = re.compile(r'-{2,}') def sanitize(value): """A name reduced to something safe to use as an identifier. Whitespace and slashes become single dashes; anything outside letters/digits/dot/dash is dropped rather than transliterated, because a guessed transliteration in a business key is worse than a shorter one. """ text = (value or '').strip() if not text: return '' text = _SEPARATORS.sub('-', text) text = _ILLEGAL.sub('', text) text = _RUNS.sub('-', text) return text.strip('-') def generate(prefix, name): """'-', or the bare name when the type has no prefix. Returns '' when there is nothing to build from, so the caller can fall back to demanding an explicit value rather than inventing one. Does NOT stack an existing prefix: a name already starting with the prefix (IDF-03 under type IDF) is returned as-is, because IDF-IDF-03 is nobody's intent. Matched case-insensitively on the prefix plus its dash. """ cleanname = sanitize(name) if not cleanname: return '' cleanprefix = sanitize(prefix).upper() if not cleanprefix: return cleanname if cleanname.upper().startswith(cleanprefix + '-'): return cleanname return '{}-{}'.format(cleanprefix, cleanname) def generate_for_type(networkdevicetypeid, name): """Same, looking the prefix up from the device type. '' if no type given.""" if not networkdevicetypeid: return sanitize(name) from shopdb.api import db from ..models import NetworkDeviceType devicetype = db.session.get(NetworkDeviceType, networkdevicetypeid) return generate(devicetype.prefix if devicetype else None, name)