Scan Points Computation Documentation
This code provides functions to compute scan points for attendance summaries. It processes scan logs and applies rules to determine check-in, check-out, and break times.
Constants
const (
SCAN_CHECK_IN ScanPointEvent = "CheckIn"
SCAN_CHECK_OUT ScanPointEvent = "CheckOut"
SCAN_BREAK ScanPointEvent = "Break"
SCAN_BREAK_IN ScanPointEvent = "BreakIn-" // always have index, even though only 1 break
SCAN_BREAK_OUT ScanPointEvent = "BreakOut-" // always have index, even though only 1 break
)
const (
DEFAULT_TRESHOLD float64 = 120 // not used
DEFAULT_NO_INDEX int = -1
DEFAULT_TRESHOLD_MULTIPLIER float64 = 0.75
MINIMUM_TRESHOLD float64 = 45
MAX_FLOAT float64 = 1500 // for function findMinimum, we need to start with large number. 1500 minutes is already sufficient, it's more than 24 hours
TRESHOLD_COUNT_EQUAL bool = true // if this is not true then all checking about threshold is < or > instead of <= and >=
SCAN_POINT_FULL_SCORE int = 1000 // just for scoring
SUMMARY_NO_SCHEDULE = "NoSchedule"
SUMMARY_NO_SHIFT = "NoShift"
SUMMARY_CANNOT_COMPUTE = "CannotCompute"
SUMMARY_INCOMPLETE = "Incomplete" // incomplete scans
SUMMARY_DAY_NO_WORK = "NoWork"
SUMMARY_DAY_WORK = "Work"
)
ScanPointStruct struct
type ScanPointStruct struct {
ScanDateTime time.Time
Treshold float64 // depends on the duration of CI-CO * DEFAULT_TRESHOLD_MULTIPLIER
WinnerLogID string // Don't need this, can get it from WinnerLogPtr.Id
WinnerRuleID string
WinnerLogPtr *entity.AttendanceLogTable // if winner, then this is the log address (from global &AllScanLogs[ID])
UseThisTime time.Time // from Winner or overwrite , can be different from WinnerLogPtr.TimeStamp
WinnerLogDistance float64
WinnerFromLogs bool
WinnerFromRule bool
WinnerFromOverwrite bool
}
ScanDateTime time.Time: The date and time of the scan point.Treshold float64: The threshold for matching scan logs.WinnerLogID string: The ID of the winning scan log.WinnerRuleID string: The ID of the winning rule.WinnerLogPtr *entity.AttendanceLogTable: Pointer to the winning scan log.UseThisTime time.Time: The time to use for this scan point.WinnerLogDistance float64: The distance of the winning scan log.WinnerFromLogs bool: Indicates if the winner is from scan logs.WinnerFromRule bool: Indicates if the winner is from a rule.WinnerFromOverwrite bool: Indicates if the winner is from an overwrite.
Functions
GetScanPointsFromShift
func GetScanPointsFromShift(iShift *entity.ShiftTable, calcDate string, cache *caching.AllCacheStruct) (ScanPointMap, []ScanPointEvent)
Generates scan points from a shift for a given calculation date.
iShift *entity.ShiftTable: The shift table.calcDate string: The calculation date inyyyy-mm-ddformat.cache *caching.AllCacheStruct: The cache structure.
Returns a map of scan points and an order of scan point events.
GetTresholdFromIntDuration
func GetTresholdFromIntDuration(dur float64, cache *caching.AllCacheStruct) float64
Calculates the threshold duration in minutes with checks on MINIMUM_TRESHOLD.
dur float64: The duration in minutes.cache *caching.AllCacheStruct: The cache structure.
Returns the threshold duration.
FindMinimumAbsolute
func FindMinimumAbsolute(scanLogs []*entity.AttendanceLogTable, eventIdx string) (*entity.AttendanceLogTable, int)
Finds the minimum absolute distance for an event index.
scanLogs []*entity.AttendanceLogTable: The scan logs.eventIdx string: The event index.
Returns a pointer to the minimum scan log and its index.
ScanLogsHasDate
func ScanLogsHasDate(calcDate string, scanLogs []*entity.AttendanceLogTable, summary *AttendanceSummariesCompute) bool
Checks if the scan logs contain the calculation date.
calcDate string: The calculation date inyyyy-mm-ddformat.scanLogs []*entity.AttendanceLogTable: The scan logs.summary *AttendanceSummariesCompute: The attendance summary.
Returns true if the scan logs contain the date, otherwise false.
GetScanPointCandidateFromScanLogs
func GetScanPointCandidateFromScanLogs(scanpoints ScanPointMap, scanLogs []*entity.AttendanceLogTable, order []ScanPointEvent)
Gets candidates of scan logs for each scan point.
scanpoints ScanPointMap: The scan points.scanLogs []*entity.AttendanceLogTable: The scan logs.order []ScanPointEvent: The order of scan point events.
ResetScanLogsUsed
func ResetScanLogsUsed(scanLogs []*entity.AttendanceLogTable)
Resets the Used and UsedAs fields of the scan logs.
scanLogs []*entity.AttendanceLogTable: The scan logs.
CheckScanPointComplete
func (scanpoints ScanPointMap) CheckScanPointComplete() (bool, []ScanPointEvent)
Checks if the scan points are complete.
scanpoints ScanPointMap: The scan points.
Returns true if the scan points are complete, otherwise false, and a list of incomplete scan point events.
GetAllDistancesFromCandidates
func (s ScanPointMap) GetAllDistancesFromCandidates() float64
Gets the total distance from all scan point candidates.
s ScanPointMap: The scan points.
Returns the total distance.
Example Usage
package main
import (
"fio-backend/entity"
"fio-backend/utils/caching"
"fio-backend/utils/compute"
"fmt"
"time"
)
func main() {
// Create a sample shift table
shift := &entity.ShiftTable{
StartTime: "08:00:00",
EndTime: "17:00:00",
ShiftBreaks: []entity.ShiftBreakTable{
{
OrderNum: 1,
BreakID: "break_id_1",
Break: entity.BreakTable{
StartTime: "12:00:00",
EndTime: "12:30:00",
DurationMinutes: 30,
},
},
},
}
// Create a sample cache
cache := &caching.AllCacheStruct{
Summaries: &caching.LocalSummaries{
Threshold: 0.75,
},
}
// Get scan points from shift
calcDate := "2024-05-01"
scanPoints, order := compute.GetScanPointsFromShift(shift, calcDate, cache)
// Print the scan points
for event, sp := range scanPoints {
fmt.Printf("Event: %s, ScanPoint: %+v\n", event, sp)
}
// Create sample scan logs
scanLogs := []*entity.AttendanceLogTable{
{
Id: "log_id_1",
UserProfileID: "user_id_123",
Timestamp: "2024-05-01T08:00:00Z",
},
{
Id: "log_id_2",
UserProfileID: "user_id_123",
Timestamp: "2024-05-01T17:00:00Z",
},
{
Id: "log_id_3",
UserProfileID: "user_id_123",
Timestamp: "2024-05-01T12:00:00Z",
},
{
Id: "log_id_4",
UserProfileID: "user_id_123",
Timestamp: "2024-05-01T12:30:00Z",
},
}
// Get scan point candidates from scan logs
compute.GetScanPointCandidateFromScanLogs(scanPoints, scanLogs, order)
// Print the updated scan points
for event, sp := range scanPoints {
fmt.Printf("Event: %s, ScanPoint: %+v\n", event, sp)
}
// Check if scan points are complete
complete, incomplete := scanPoints.CheckScanPointComplete()
fmt.Printf("Complete: %v, Incomplete: %+v\n", complete, incomplete)
}
Improvements and Considerations
- Error Handling: Improve error handling by returning errors instead of just logging them.
- Code Clarity and Comments: Improve code comments to explain complex logic more clearly.
- Unit Tests: Add unit tests to ensure the functions work correctly with various inputs and edge cases.
- Performance Optimization: Optimize the functions for performance, especially when processing large numbers of scan logs and breaks.
- Modularization: Break down the functions into smaller, more modular functions to improve readability and maintainability.
This comprehensive documentation provides a detailed understanding of the scan points computation functions and their functionalities. By addressing the suggested improvements, you can create a more robust and maintainable solution for calculating scan points in attendance summaries.