farmcontrol-ui/src/database/ObjectModels.js

222 lines
5.6 KiB
JavaScript

import { Printer } from './models/Printer.js'
import { Host } from './models/Host.js'
import { Filament } from './models/Filament.js'
import { Spool } from './models/Spool'
import { GCodeFile } from './models/GCodeFile'
import { Job } from './models/Job'
import { Product } from './models/Product'
import { Part } from './models/Part.js'
import { Vendor } from './models/Vendor'
import { File } from './models/File'
import { SubJob } from './models/SubJob'
import { Initial } from './models/Initial'
import { FilamentStock } from './models/FilamentStock'
import { StockEvent } from './models/StockEvent'
import { StockAudit } from './models/StockAudit'
import { PartStock } from './models/PartStock'
import { ProductStock } from './models/ProductStock'
import { PurchaseOrder } from './models/PurchaseOrder'
import { AuditLog } from './models/AuditLog'
import { User } from './models/User'
import { NoteType } from './models/NoteType'
import { Note } from './models/Note'
import { DocumentSize } from './models/DocumentSize.js'
import { DocumentTemplate } from './models/DocumentTemplate.js'
import { DocumentPrinter } from './models/DocumentPrinter.js'
import { DocumentJob } from './models/DocumentJob.js'
import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'
export const objectModels = [
Printer,
Host,
Filament,
Spool,
GCodeFile,
Job,
Product,
Part,
Vendor,
File,
SubJob,
Initial,
FilamentStock,
StockEvent,
StockAudit,
PartStock,
ProductStock,
PurchaseOrder,
AuditLog,
User,
NoteType,
Note,
DocumentSize,
DocumentTemplate,
DocumentPrinter,
DocumentJob
]
// Re-export individual models for direct access
export {
Printer,
Host,
Filament,
Spool,
GCodeFile,
Job,
Product,
Part,
Vendor,
File,
SubJob,
Initial,
FilamentStock,
StockEvent,
StockAudit,
PartStock,
ProductStock,
PurchaseOrder,
AuditLog,
User,
NoteType,
Note,
DocumentSize,
DocumentTemplate,
DocumentPrinter,
DocumentJob
}
export function getModelByName(name, ignoreCase = false) {
function formatName(formattedName) {
if (ignoreCase == true) {
formattedName = formattedName.toUpperCase()
}
return formattedName
}
return (
objectModels.find((meta) => formatName(meta.name) === formatName(name)) || {
name: 'unknown',
label: 'Unknown',
prefix: 'UNK',
icon: QuestionCircleIcon,
url: () => '#',
properties: {}
}
)
}
export function getModelProperty(name, property) {
const model = getModelByName(name)
if (!model || !model.properties) {
return undefined
}
return model.properties.find((prop) => prop.name == property)
}
export function getModelProperties(name, propertyList) {
const model = getModelByName(name)
if (!model || !model.properties) {
return []
}
// If no propertyList is provided, return all properties
if (!propertyList || propertyList.length === 0) {
return model.properties
}
// Create a map of property names to properties for efficient lookup
const propertyMap = new Map(
model.properties.map((property) => [property.name, property])
)
// Return properties in the same order as propertyList
return propertyList
.map((propertyName) => propertyMap.get(propertyName))
.filter((property) => property !== undefined)
}
export function getModelByPrefix(prefix) {
return (
objectModels.find((meta) => meta.prefix === prefix) || {
name: 'unknown',
label: 'Unknown',
prefix: 'UNK',
icon: QuestionCircleIcon,
url: () => '#',
properties: {}
}
)
}
// Utility function to get nested object values
export const getPropertyValue = (obj, path) => {
if (!obj || !path) return undefined
if (path.includes('.')) {
const propertyPath = path.split('.')
let currentValue = obj
for (const prop of propertyPath) {
if (currentValue && typeof currentValue === 'object') {
currentValue = currentValue[prop]
} else {
currentValue = undefined
break
}
}
return currentValue
} else {
return obj[path]
}
}
export const evaluateVariable = (expression, data) => {
if (!expression) return false
// Only treat as an expression if it starts and ends with ()
const expr = expression.trim()
if (!(expr.startsWith('(') && expr.endsWith(')'))) return false
// Remove the outer parentheses
const innerExpr = expr.slice(1, -1)
// Helper to evaluate a single condition like 'foo == "bar"' or 'foo.bar == 42' or 'foo == true'
const evalCondition = (cond, data) => {
const match = cond.trim().match(/^([a-zA-Z0-9_.]+)\s*==\s*(.+)$/)
if (!match) return false
const [, path, valueRaw] = match
let value
let raw = valueRaw.trim()
// Check for quoted string
if (
(raw.startsWith('"') && raw.endsWith('"')) ||
(raw.startsWith("'") && raw.endsWith("'"))
) {
value = raw.slice(1, -1)
} else if (raw === 'true') {
value = true
} else if (raw === 'false') {
value = false
} else if (!isNaN(Number(raw))) {
value = Number(raw)
} else {
value = raw
}
// Resolve nested property
const propValue = path
.split('.')
.reduce((acc, key) => (acc ? acc[key] : undefined), data)
return propValue === value
}
// Split by '||' first (lowest precedence)
const orParts = innerExpr.split(/\|\|/)
for (let orPart of orParts) {
// Each orPart may have '&&' (higher precedence)
const andParts = orPart.split(/&&/)
const andResult = andParts.every((andPart) => evalCondition(andPart, data))
if (andResult) return true // If any OR group is true, return true
}
return false // None of the OR groups were true
}