Collector: ingest GE-Enforce/enrollment data with configurable pc-type mapping
Extends the computers collector so it can replace the classic api.asp updateCompleteAsset path that the shopfloor PC fleet uses to auto-update data. Collector schema (project naming) now accepts the GE-Enforce/enrollment shape: machinenumber, pctype, pcsubtype, serialnumber, loggedinuser, lastboottime, lastcheckin, ipaddress, vendorname, modelnumber, osname, installedsoftware. - machinenumber -> Asset.assetnumber (skips the 9999 imaging placeholder, falls back to hostname), on create and update. - pctype -> ComputerType via a configurable mapping (see below). - vendor/model created if missing (free vocab); OS looked up (controlled, warns if unknown); pcsubtype accepted but not yet stored (warning). - Dropped per scope: VNC/WinRM flags, warranty, DNC config, multi-NIC. Configurable pc-type mapping (the gea-shopfloor-* imaging taxonomy -> ComputerType): defaults + resolution live in plugins/computers/pctypemap.py (plugin domain, contract-pure - reads Setting via shopdb.api); overrides stored as pctypemap_<pxetype> settings, seeded on plugin install, edited in Settings > System > "Collector PC Type Mapping" (new UI section). Migration doc: docs/COLLECTOR-INTEGRATION.md maps classic api.asp fields + GE-Enforce status fields to the collector schema, documents machine-number sourcing (registry MachineNo first, then C:\Enrollment\machine-number.txt) and that the transport is interim. Tests: complete-asset payload maps machinenumber/pctype/vendor/model/os; 9999 placeholder falls back to hostname. 186 tests pass, naming green, app boots, mapping UI verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -440,6 +440,41 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Collector PC Type Mapping Section -->
|
||||
<div class="section-card" v-if="pcTypeMappings.length">
|
||||
<h2 class="section-title">Collector PC Type Mapping</h2>
|
||||
|
||||
<div class="setting-group">
|
||||
<p class="setting-description">
|
||||
When the collector ingests a PC, its imaging pc-type (from
|
||||
C:\Enrollment\pc-type.txt) is mapped to one of your Computer Types.
|
||||
Adjust the mapping per site.
|
||||
</p>
|
||||
|
||||
<div class="table-container">
|
||||
<table class="identifier-matrix">
|
||||
<thead>
|
||||
<tr><th>Imaging pc-type</th><th>Computer Type</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in pcTypeMappings" :key="row.pxetype">
|
||||
<td class="identifier-name">{{ row.pxetype }}</td>
|
||||
<td>
|
||||
<select
|
||||
:value="row.computertype"
|
||||
@change="changePcTypeMapping(row.pxetype, $event.target.value)"
|
||||
:disabled="saving"
|
||||
>
|
||||
<option v-for="ct in computerTypes" :key="ct" :value="ct">{{ ct }}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="error-message">{{ error }}</div>
|
||||
@@ -448,7 +483,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { settingsApi } from '../../api'
|
||||
import { settingsApi, computersApi } from '../../api'
|
||||
import { setIdentifierFlag } from '../../composables/identifierSettings'
|
||||
|
||||
const settings = reactive({
|
||||
@@ -522,6 +557,10 @@ function searchValue(domainKey) {
|
||||
return key in searchMatrix ? searchMatrix[key] : true
|
||||
}
|
||||
|
||||
// Collector pc-type -> ComputerType mapping (pctypemap_<pxetype> settings).
|
||||
const pcTypeMappings = ref([]) // [{ pxetype, computertype }]
|
||||
const computerTypes = ref([]) // ComputerType names for the dropdown
|
||||
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
const testingEmail = ref(false)
|
||||
@@ -579,6 +618,7 @@ async function loadSettings() {
|
||||
loading.value = true
|
||||
const { data } = await settingsApi.list()
|
||||
|
||||
const pctypeRows = []
|
||||
for (const setting of data.data) {
|
||||
if (setting.key in settings) {
|
||||
settings[setting.key] = setting.value
|
||||
@@ -586,8 +626,22 @@ async function loadSettings() {
|
||||
identifierMatrix[setting.key] = setting.value !== false
|
||||
} else if (/^search_.+_enabled$/.test(setting.key)) {
|
||||
searchMatrix[setting.key] = setting.value !== false
|
||||
} else if (setting.key.startsWith('pctypemap_')) {
|
||||
pctypeRows.push({
|
||||
pxetype: setting.key.slice('pctypemap_'.length),
|
||||
computertype: setting.value
|
||||
})
|
||||
}
|
||||
}
|
||||
pcTypeMappings.value = pctypeRows.sort((a, b) => a.pxetype.localeCompare(b.pxetype))
|
||||
|
||||
// Computer type options for the mapping dropdown.
|
||||
try {
|
||||
const typesResponse = await computersApi.types.list({ perpage: 100 })
|
||||
computerTypes.value = (typesResponse.data.data || []).map(t => t.computertype)
|
||||
} catch (typesError) {
|
||||
console.error('Failed to load computer types', typesError)
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = 'Failed to load settings'
|
||||
console.error(e)
|
||||
@@ -596,6 +650,25 @@ async function loadSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
async function changePcTypeMapping(pxetype, computertype) {
|
||||
const key = `pctypemap_${pxetype}`
|
||||
try {
|
||||
saving.value = true
|
||||
error.value = ''
|
||||
success.value = ''
|
||||
await settingsApi.update(key, computertype)
|
||||
const row = pcTypeMappings.value.find(r => r.pxetype === pxetype)
|
||||
if (row) row.computertype = computertype
|
||||
success.value = 'Setting saved'
|
||||
setTimeout(() => { success.value = '' }, 2000)
|
||||
} catch (e) {
|
||||
error.value = e.response?.data?.message || 'Failed to save setting'
|
||||
console.error(e)
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleSetting(key) {
|
||||
const newValue = !settings[key]
|
||||
await saveSetting(key, newValue)
|
||||
|
||||
Reference in New Issue
Block a user