Model type shown only when it differs from the asset type
This commit is contained in:
@@ -105,9 +105,13 @@
|
|||||||
<span class="info-label">Model</span>
|
<span class="info-label">Model</span>
|
||||||
<span class="info-value">{{ computer.computer?.modelname || '-' }}</span>
|
<span class="info-value">{{ computer.computer?.modelname || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- The catalog model's own type. Kept separate from this
|
<!-- Only when it DIFFERS from this asset's own type. The two are
|
||||||
asset's type: modeltypes spans every kind of asset. -->
|
separate tables - modeltypes spans every kind of asset - but
|
||||||
<div class="info-row" v-if="computer.computer?.modeltypename">
|
they carry the same names in practice, so showing both
|
||||||
|
always put two identical Type rows in one section. Worth
|
||||||
|
seeing only when they disagree. -->
|
||||||
|
<div class="info-row"
|
||||||
|
v-if="computer.computer?.modeltypename && computer.computer.modeltypename !== computer.computer?.computertypename">
|
||||||
<span class="info-label">Model type</span>
|
<span class="info-label">Model type</span>
|
||||||
<span class="info-value">{{ computer.computer.modeltypename }}</span>
|
<span class="info-value">{{ computer.computer.modeltypename }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -124,9 +124,13 @@
|
|||||||
<span class="info-label">Model</span>
|
<span class="info-label">Model</span>
|
||||||
<span class="info-value">{{ machine.machine?.modelname || '-' }}</span>
|
<span class="info-value">{{ machine.machine?.modelname || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- The catalog model's own type. Kept separate from this
|
<!-- Only when it DIFFERS from this asset's own type. The two are
|
||||||
asset's type: modeltypes spans every kind of asset. -->
|
separate tables - modeltypes spans every kind of asset - but
|
||||||
<div class="info-row" v-if="machine.machine?.modeltypename">
|
they carry the same names in practice, so showing both
|
||||||
|
always put two identical Type rows in one section. Worth
|
||||||
|
seeing only when they disagree. -->
|
||||||
|
<div class="info-row"
|
||||||
|
v-if="machine.machine?.modeltypename && machine.machine.modeltypename !== machine.machine?.machinetypename">
|
||||||
<span class="info-label">Model type</span>
|
<span class="info-label">Model type</span>
|
||||||
<span class="info-value">{{ machine.machine.modeltypename }}</span>
|
<span class="info-value">{{ machine.machine.modeltypename }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -98,9 +98,10 @@
|
|||||||
<span class="info-label">Model</span>
|
<span class="info-label">Model</span>
|
||||||
<span class="info-value">{{ device.networkdevice.modelname }}</span>
|
<span class="info-value">{{ device.networkdevice.modelname }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- The catalog model's own type, kept separate from the device
|
<!-- Only when it DIFFERS from the device's own type: the two are
|
||||||
type: modeltypes spans every kind of asset. -->
|
separate tables but carry the same names in practice. -->
|
||||||
<div class="info-row" v-if="device.networkdevice?.modeltypename">
|
<div class="info-row"
|
||||||
|
v-if="device.networkdevice?.modeltypename && device.networkdevice.modeltypename !== device.networkdevice?.networkdevicetypename">
|
||||||
<span class="info-label">Model type</span>
|
<span class="info-label">Model type</span>
|
||||||
<span class="info-value">{{ device.networkdevice.modeltypename }}</span>
|
<span class="info-value">{{ device.networkdevice.modeltypename }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -49,9 +49,10 @@
|
|||||||
<span class="hero-detail-label">Model</span>
|
<span class="hero-detail-label">Model</span>
|
||||||
<span class="hero-detail-value">{{ printer.printer.modelname }}</span>
|
<span class="hero-detail-value">{{ printer.printer.modelname }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- The catalog model's own type, kept separate from the printer's
|
<!-- Only when it DIFFERS from the printer's own type: the two are
|
||||||
type: modeltypes spans every kind of asset. -->
|
separate tables but carry the same names in practice. -->
|
||||||
<div class="hero-detail" v-if="printer.printer?.modeltypename">
|
<div class="hero-detail"
|
||||||
|
v-if="printer.printer?.modeltypename && printer.printer.modeltypename !== printer.printer?.printertypename">
|
||||||
<span class="hero-detail-label">Model type</span>
|
<span class="hero-detail-label">Model type</span>
|
||||||
<span class="hero-detail-value">{{ printer.printer.modeltypename }}</span>
|
<span class="hero-detail-value">{{ printer.printer.modeltypename }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,11 +10,18 @@ ONLY fills rows where the asset's vendor is NULL and the model names one. It
|
|||||||
never overwrites a vendor somebody chose, and it never guesses: if the model has
|
never overwrites a vendor somebody chose, and it never guesses: if the model has
|
||||||
no vendor either, the row is left alone.
|
no vendor either, the row is left alone.
|
||||||
|
|
||||||
DELIBERATELY NOT DONE HERE: the asset's TYPE. modeltypes is the catalog-wide
|
It also fills the asset's TYPE from the model's type, but ONLY where the two
|
||||||
list covering every kind of asset - it holds "Access Point", "Camera" and
|
names are identical. modeltypes is the catalog-wide list covering every kind of
|
||||||
"Desktop PC" alongside the machine entries - so it is a different taxonomy from
|
asset - it holds "Access Point", "Camera" and "Desktop PC" alongside the machine
|
||||||
machinetypes, and mapping one onto the other by name would mistype whatever does
|
entries - so it is a different taxonomy from machinetypes and the two cannot be
|
||||||
not match. Only about two thirds of the names overlap.
|
equated in general. Across the whole catalog only about two thirds of the names
|
||||||
|
overlap. Restricted to the models an asset class actually uses the picture is
|
||||||
|
different: every one of the 262 machines in the development database maps
|
||||||
|
exactly, because the non-machine entries are never used by machines.
|
||||||
|
|
||||||
|
So the rule is exact name match or nothing. A model type with no same-named
|
||||||
|
entry in the asset's own type table is REPORTED and left alone, never guessed
|
||||||
|
at, because the failure mode is a machine labelled "Desktop PC".
|
||||||
|
|
||||||
Dry run by default; nothing is written without --commit.
|
Dry run by default; nothing is written without --commit.
|
||||||
|
|
||||||
@@ -38,6 +45,16 @@ TARGETS = [
|
|||||||
('networkdevices', 'networkdeviceid', 'hostname'),
|
('networkdevices', 'networkdeviceid', 'hostname'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# The asset's own type column, and the table it points at. Each asset class
|
||||||
|
# keeps its own type list; the catalog's modeltypes is a different taxonomy, so
|
||||||
|
# these are joined by NAME and only when the name matches exactly.
|
||||||
|
TYPE_TARGETS = {
|
||||||
|
'machines': ('machinetypeid', 'machinetypes', 'machinetype'),
|
||||||
|
'computers': ('computertypeid', 'computertypes', 'computertype'),
|
||||||
|
'printers': ('printertypeid', 'printertypes', 'printertype'),
|
||||||
|
'networkdevices': ('networkdevicetypeid', 'networkdevicetypes', 'networkdevicetype'),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def resolve(connection, table, idcol, labelcol):
|
def resolve(connection, table, idcol, labelcol):
|
||||||
"""Rows that would change, newest table columns tolerated."""
|
"""Rows that would change, newest table columns tolerated."""
|
||||||
@@ -62,6 +79,46 @@ def resolve(connection, table, idcol, labelcol):
|
|||||||
return label, rows
|
return label, rows
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_types(connection, table, idcol, labelcol):
|
||||||
|
"""Rows whose type could be taken from the model, plus the ones that cannot.
|
||||||
|
|
||||||
|
Returns (fillable, unmatched). unmatched rows have a model type that has no
|
||||||
|
identically named entry in this asset's own type table, and are never
|
||||||
|
touched - they are reported so somebody can decide.
|
||||||
|
"""
|
||||||
|
from sqlalchemy import inspect, text
|
||||||
|
spec = TYPE_TARGETS.get(table)
|
||||||
|
if not spec:
|
||||||
|
return [], []
|
||||||
|
typecol, typetable, typenamecol = spec
|
||||||
|
columns = {c['name'] for c in inspect(connection).get_columns(table)}
|
||||||
|
if 'modelnumberid' not in columns or typecol not in columns:
|
||||||
|
return [], []
|
||||||
|
label = labelcol if labelcol in columns else idcol
|
||||||
|
|
||||||
|
fillable = connection.execute(text(f"""
|
||||||
|
SELECT a.{idcol} AS assetid, a.{label} AS label,
|
||||||
|
mt.modeltype AS modeltype, tt.{typenamecol} AS typename
|
||||||
|
FROM {table} a
|
||||||
|
JOIN models mo ON a.modelnumberid = mo.modelnumberid
|
||||||
|
JOIN modeltypes mt ON mo.modeltypeid = mt.modeltypeid
|
||||||
|
JOIN {typetable} tt ON tt.{typenamecol} = mt.modeltype
|
||||||
|
WHERE a.{typecol} IS NULL
|
||||||
|
ORDER BY a.{idcol}
|
||||||
|
""")).fetchall()
|
||||||
|
|
||||||
|
unmatched = connection.execute(text(f"""
|
||||||
|
SELECT mt.modeltype AS modeltype, COUNT(*) AS n
|
||||||
|
FROM {table} a
|
||||||
|
JOIN models mo ON a.modelnumberid = mo.modelnumberid
|
||||||
|
JOIN modeltypes mt ON mo.modeltypeid = mt.modeltypeid
|
||||||
|
LEFT JOIN {typetable} tt ON tt.{typenamecol} = mt.modeltype
|
||||||
|
WHERE a.{typecol} IS NULL AND tt.{typenamecol} IS NULL
|
||||||
|
GROUP BY mt.modeltype ORDER BY n DESC
|
||||||
|
""")).fetchall()
|
||||||
|
return fillable, unmatched
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description=__doc__,
|
parser = argparse.ArgumentParser(description=__doc__,
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
@@ -108,6 +165,31 @@ def main():
|
|||||||
AND m.vendorid IS NOT NULL
|
AND m.vendorid IS NOT NULL
|
||||||
"""))
|
"""))
|
||||||
|
|
||||||
|
# Type, by exact name only.
|
||||||
|
typed, unmatched = resolve_types(connection, table, idcol, labelcol)
|
||||||
|
spec = TYPE_TARGETS.get(table)
|
||||||
|
print(f'{table}: {len(typed)} row(s) would get a type')
|
||||||
|
for row in typed[:args.limit]:
|
||||||
|
print(f' {row.assetid:>7} {str(row.label)[:28]:<28} '
|
||||||
|
f'model type {row.modeltype[:22]:<22} -> {row.typename}')
|
||||||
|
if len(typed) > args.limit:
|
||||||
|
print(f' ... and {len(typed) - args.limit} more')
|
||||||
|
for row in unmatched:
|
||||||
|
print(f' LEFT ALONE: {row.n} row(s) with model type '
|
||||||
|
f'"{row.modeltype}" - no matching {spec[1]} entry')
|
||||||
|
total += len(typed)
|
||||||
|
|
||||||
|
if args.commit and typed:
|
||||||
|
typecol, typetable, typenamecol = spec
|
||||||
|
connection.execute(text(f"""
|
||||||
|
UPDATE {table} a
|
||||||
|
JOIN models mo ON a.modelnumberid = mo.modelnumberid
|
||||||
|
JOIN modeltypes mt ON mo.modeltypeid = mt.modeltypeid
|
||||||
|
JOIN {typetable} tt ON tt.{typenamecol} = mt.modeltype
|
||||||
|
SET a.{typecol} = tt.{typecol}
|
||||||
|
WHERE a.{typecol} IS NULL
|
||||||
|
"""))
|
||||||
|
|
||||||
if args.commit:
|
if args.commit:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
print(f'\nCommitted. {total} row(s) updated.')
|
print(f'\nCommitted. {total} row(s) updated.')
|
||||||
|
|||||||
Reference in New Issue
Block a user