API stats implementation.
This commit is contained in:
parent
0a1540325b
commit
f2036a7a69
@ -101,6 +101,7 @@ const ApiServerProvider = ({ children }) => {
|
||||
newSocket.on('objectNew', handleObjectNew)
|
||||
newSocket.on('objectDelete', handleObjectDelete)
|
||||
newSocket.on('lockUpdate', handleLockUpdate)
|
||||
newSocket.on('modelStats', handleModelStats)
|
||||
|
||||
newSocket.on('disconnect', () => {
|
||||
logger.debug('Api Server disconnected')
|
||||
@ -248,6 +249,33 @@ const ApiServerProvider = ({ children }) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleModelStats = async (data) => {
|
||||
const objectType = data.objectType || 'unknown'
|
||||
|
||||
const callbacksRefKey = `modelStats:${objectType}`
|
||||
logger.debug('Notifying model stats update:', data)
|
||||
console.log('handleModelStats', data)
|
||||
if (objectType && subscribedCallbacksRef.current.has(callbacksRefKey)) {
|
||||
const callbacks = subscribedCallbacksRef.current.get(callbacksRefKey)
|
||||
logger.debug(
|
||||
`Calling ${callbacks.length} callbacks for model stats:`,
|
||||
callbacksRefKey
|
||||
)
|
||||
callbacks.forEach((callback) => {
|
||||
try {
|
||||
callback(data.stats)
|
||||
} catch (error) {
|
||||
logger.error('Error in model stats callback:', error)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
logger.debug(
|
||||
`No callbacks found for model stats: ${callbacksRefKey}, subscribed callbacks:`,
|
||||
Array.from(subscribedCallbacksRef.current.keys())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const handleObjectNew = async (data) => {
|
||||
logger.debug('Notifying object new:', data)
|
||||
const objectType = data.objectType || 'unknown'
|
||||
@ -514,6 +542,67 @@ const ApiServerProvider = ({ children }) => {
|
||||
[offObjectUpdatesEvent]
|
||||
)
|
||||
|
||||
const offModelStats = useCallback((objectType, callback) => {
|
||||
if (socketRef.current && socketRef.current.connected == true) {
|
||||
const callbacksRefKey = `modelStats:${objectType}`
|
||||
|
||||
// Remove callback from the subscribed callbacks map
|
||||
if (subscribedCallbacksRef.current.has(callbacksRefKey)) {
|
||||
const callbacks = subscribedCallbacksRef.current
|
||||
.get(callbacksRefKey)
|
||||
.filter((cb) => cb !== callback)
|
||||
if (callbacks.length === 0) {
|
||||
subscribedCallbacksRef.current.delete(callbacksRefKey)
|
||||
socketRef.current.emit('unsubscribeModelStats', {
|
||||
objectType
|
||||
})
|
||||
} else {
|
||||
subscribedCallbacksRef.current.set(callbacksRefKey, callbacks)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const subscribeToModelStats = useCallback(
|
||||
(objectType, callback) => {
|
||||
if (socketRef.current && socketRef.current.connected == true) {
|
||||
const callbacksRefKey = `modelStats:${objectType}`
|
||||
// Add callback to the subscribed callbacks map immediately
|
||||
if (!subscribedCallbacksRef.current.has(callbacksRefKey)) {
|
||||
subscribedCallbacksRef.current.set(callbacksRefKey, [])
|
||||
}
|
||||
|
||||
const callbacksLength =
|
||||
subscribedCallbacksRef.current.get(callbacksRefKey).length
|
||||
|
||||
if (callbacksLength <= 0) {
|
||||
socketRef.current.emit(
|
||||
'subscribeToModelStats',
|
||||
{
|
||||
objectType: objectType
|
||||
},
|
||||
(result) => {
|
||||
if (result.success) {
|
||||
logger.info('Subscribed to model stats:', objectType)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
logger.info(
|
||||
'Adding model stats callback:',
|
||||
objectType,
|
||||
'callbacks length:',
|
||||
callbacksLength + 1
|
||||
)
|
||||
subscribedCallbacksRef.current.get(callbacksRefKey).push(callback)
|
||||
|
||||
// Return cleanup function
|
||||
return () => offModelStats(objectType, callback)
|
||||
}
|
||||
},
|
||||
[offModelStats]
|
||||
)
|
||||
|
||||
const subscribeToObjectLock = useCallback(
|
||||
(id, type, callback) => {
|
||||
logger.debug('Subscribing to lock for object:', id, 'type:', type)
|
||||
@ -834,6 +923,106 @@ const ApiServerProvider = ({ children }) => {
|
||||
}
|
||||
}
|
||||
|
||||
const getModelStats = async (objectType) => {
|
||||
logger.debug('Fetching stats for model type:', objectType)
|
||||
try {
|
||||
let statsUrl
|
||||
if (objectType === 'history') {
|
||||
statsUrl = `${config.backendUrl}/stats/history`
|
||||
} else {
|
||||
statsUrl = `${config.backendUrl}/${objectType.toLowerCase()}s/stats`
|
||||
}
|
||||
|
||||
const response = await axios.get(statsUrl, {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
logger.debug('Fetched stats for model type:', objectType)
|
||||
return response.data
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
showError(err, () => {
|
||||
getModelStats(objectType)
|
||||
})
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const getModelHistory = async (objectType, startDate, endDate) => {
|
||||
logger.debug('Fetching history for model type:', objectType)
|
||||
const encodedStartDate = encodeURIComponent(startDate.toISOString())
|
||||
const encodedEndDate = encodeURIComponent(endDate.toISOString())
|
||||
try {
|
||||
const historyUrl = `${config.backendUrl}/${objectType.toLowerCase()}s/history?from=${encodedStartDate}&to=${encodedEndDate}`
|
||||
|
||||
const response = await axios.get(historyUrl, {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
logger.debug('Fetched history for model type:', objectType)
|
||||
|
||||
// Calculate time range in milliseconds
|
||||
const timeRangeMs = endDate.getTime() - startDate.getTime()
|
||||
const oneHourMs = 60 * 60 * 1000
|
||||
const twelveHoursMs = 12 * 60 * 60 * 1000
|
||||
|
||||
// Determine interval based on time range
|
||||
let intervalMinutes = 1 // Default: 1 minute
|
||||
if (timeRangeMs > twelveHoursMs) {
|
||||
intervalMinutes = 10 // Over 12 hours: 10 minutes
|
||||
} else if (timeRangeMs > oneHourMs) {
|
||||
intervalMinutes = 5 // Over 1 hour: 5 minutes
|
||||
}
|
||||
|
||||
const intervalMs = intervalMinutes * 60 * 1000
|
||||
|
||||
// Filter data to only include points at the specified intervals
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
const filteredData = []
|
||||
const seenIntervals = new Set()
|
||||
|
||||
response.data.forEach((point) => {
|
||||
const pointDate = new Date(point.date)
|
||||
// Round down to the nearest interval
|
||||
const roundedTime =
|
||||
Math.floor(pointDate.getTime() / intervalMs) * intervalMs
|
||||
const roundedDate = new Date(roundedTime)
|
||||
const intervalKey = roundedDate.toISOString()
|
||||
|
||||
// Only include if we haven't seen this interval yet
|
||||
if (!seenIntervals.has(intervalKey)) {
|
||||
seenIntervals.add(intervalKey)
|
||||
// Update the point's date to the rounded interval
|
||||
filteredData.push({
|
||||
...point,
|
||||
date: roundedDate.toISOString()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Sort by date to ensure chronological order
|
||||
filteredData.sort((a, b) => new Date(a.date) - new Date(b.date))
|
||||
|
||||
logger.debug(
|
||||
`Filtered history data: ${response.data.length} -> ${filteredData.length} points (${intervalMinutes} min intervals)`
|
||||
)
|
||||
return filteredData
|
||||
}
|
||||
|
||||
return response.data
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
showError(err, () => {
|
||||
getModelHistory(objectType)
|
||||
})
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const fetchTemplatePreview = async (
|
||||
id,
|
||||
content,
|
||||
@ -1032,10 +1221,13 @@ const ApiServerProvider = ({ children }) => {
|
||||
subscribeToObjectEvent,
|
||||
subscribeToObjectTypeUpdates,
|
||||
subscribeToObjectLock,
|
||||
subscribeToModelStats,
|
||||
fetchObject,
|
||||
fetchObjects,
|
||||
fetchObjectsByProperty,
|
||||
fetchSpotlightData,
|
||||
getModelStats,
|
||||
getModelHistory,
|
||||
fetchLoading,
|
||||
showError,
|
||||
fetchFileContent,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user