Skip to content

natserract/mini-evv-logger

Repository files navigation

Mini EVV Logger – Caregiver Shift Tracker

Caregivers are assigned daily shifts to visit clients at their homes. As part of Electronic Visit Verification (EVV) compliance, each caregiver must log their visit by capturing timestamps and real-time location (latitude & longitude).

Table of Contents

Features

  • Shift scheduling: plan and assign shifts for caregivers.
  • Clock-in / clock-out tracking: log when caregivers start and end their shift.
  • Task Progress Tracking: caregivers can mark tasks as completed or not completed (with reason)
  • Geolocation Integration: Uses browser's Geolocation API for capture user location, with fallback handling

Technical Architecture

This project involves creating a backend system for a caregiver shift tracker where users can manage their shifts, track their visits, and monitor task progress.

Hertz Http

Hertz is a high-performance HTTP framework for Go. It is designed to be fast, lightweight, and easy to use. It provides a simple and intuitive API for building web applications and APIs.

Benchmark:

bench

Database Design

Database Design

The database uses Postgres. The schema includes the following tables:

users

Table for storing user information.

schedules

Table for storing schedule information. It defines the template or rules for occurrences (e.g., timing, recurrence, timezone, grace period).

  • One-time: Represents a single, non-recurring event that occurs at a specific date and time.
  • Periodic: Represents a recurring event that generates multiple occurrences based on defined recurrence rules (e.g., daily, weekly).

Schedule status vs Occurrence status

schedules.status: The status column in the schedules table indicates the overall lifecycle state of the schedule, reflecting whether it is active, paused, completed, or terminated.

schedule_occurrences.status: The status column in the schedule_occurrences table indicates the state of an individual occurrence of a schedule, whether it’s planned, ongoing, completed, or missed.

tasks

Table for storing task information.

schedule_occurrences

Table tracks individual occurrences of schedules, with occurrence_start and occurrence_end defining the time window for each occurrence.

  • For one-time schedules: Creates a single occurrence with status = 'scheduled'.
  • For periodic schedules: Creates multiple occurrences based on recurrence rules, each with status = 'scheduled'.

Example:

  • A one-time schedule with start_date = '2025-09-21' and start_time = '14:00:00+08' generates one occurrence with matching occurrence_start_date and occurrence_start_time.

  • A periodic schedule with interval = 'day', interval_unit = 1, and start_date = '2025-09-21' generates daily occurrences (e.g., 2025-09-21, 2025-09-22, etc.) up to 30 days or schedule_expiry_date.

schedule_occurrences_events

Table tracks individual occurrences of schedule events. Logs specific check-in/out events for an occurrence

schedule_events

Table tracks for schedule/task changes. For example, Logs "Update progress on scheduled care activities" (task status changes).

Geolocation

To track geolocation data, I use the browser's navigator.geolocation API. But it have limitations, browser geolocation can fail due to user denial, no GPS hardware, or privacy settings. So to handle fallback mechanism, I use IP geolocation services like ipstack or ipinfo. IP fallback provides a server-side estimate based on IP address, which is less precise but always available (no user permission needed).

(@see: /ui/lib/geolocation.ts)

Task Status Transition with FSM (Finite State Machine)

A Task Status Transition with FSM is a design pattern where each task has a well-defined state (e.g., not_started, completed, cancelled), and only certain valid transitions are allowed between these states.

The FSM maps events (like complete or cancel) to transitions that move a task from one state to another.

Any attempt to perform an invalid transition is rejected, ensuring state consistency.

FSM enforces strict, predictable, and maintainable task status transitions, reducing bugs and making your workflow logic transparent and future-proof.

(@see: /internal/tasks/state_machine/task.go)

Managing Schedule Lifecycle

Background jobs monitor and update the status of schedules and occurrences. For instance, they check for missed occurrences (e.g., when the current time, 05:21 PM WIB on 21 Sep 2025, exceeds occurrence_end_date plus grace_period) and update their status to cancelled with a reason like "missed schedule."

They can also cancel periodic schedules if max_missed_occurrences is exceeded, ensuring the system responds to repeated failures.

Jobs trigger at specified intervals: every 5 minutes

Supporting Use Cases:

  1. For the one-time schedule (e.g., 15 Jan 2025, 09:00–10:00 WIB), a background job can check for missed execution and update the status.

  2. For the periodic schedule (e.g., weekly on Wednesdays), jobs generate occurrences (e.g., 15 Jan, 22 Jan 2025) and manage missed occurrences, ensuring continuity.

(@see: /internal/background_jobs/jobs/mark_missed_occurance.go)

Row Locking

Row locking is a database mechanism used to ensure data consistency when multiple users or processes (e.g., concurrent API requests) try to update the same row simultaneously. Usecase: When a caregiver updates task status (Completed/Not Completed), locking prevents race conditions that could misreport progress on the Schedule Details page.

(@see: /schema/postgres/migrations/20250918055231_task_function.sql#L18)

Logging

Zap is a structured, fast, leveled logger for Go.

(@see: pkg/logger)

Error Handling

if postgres.IsNoRows(err) {
	return nil, apperrs.NewAppError("Task not found", errs.ErrDataNotFound, consts.StatusBadRequest)
}

(@see: pkg/apperrs)

Error Monitoring

I use sentry for error monitoring. Sentry is great for catching errors, stack traces, and context (user, request, tags, etc.).

(@see: pkg/sentryutil)

API Endpoints

  • GET all schedule /v1/schedules
  • GET today schedules /v1/schedules?filter[today]=true
  • GET schedule By ID /v1/schedules/:id/occurrences/:occurrence_id
  • GET schedule stats/metrics /v1/schedules/stats
  • POST clock-in /v1/schedules/:id/occurrences/:occurrence_id/start
  • POST cancel clock-in /v1/schedules/:id/occurrences/:occurrence_id/cancel
  • POST clock-out /v1/schedules/:id/occurrences/:occurrence_id/end
  • POST update task progress /v1/tasks/:id/update

Testing

Testing is critical to ensure reliability, The current setup includes:

You can run the tests locally by executing the following command:

For backend:

make test

For frontend:

pnpm test

CI/CD

(@see: ci/cd pipeline )

Postman Collection

@see: api collection

Development

Setup

Setup environment:

cp .env.example .env

Install required packages:

go mod tidy #backend
pnpm install #frontend

Create Database: mini_evv

Migrate Database:

make migrate-up #or

goose -s -v -dir schema/postgres/migrations postgres "postgresql://USERNAME:PASSWORD@localhost:5432/mini_evv" up

Running

make dev

Running with Docker

docker build --build-arg GITHUB_TOKEN= \
--build-arg SERVICE_HOST=localhost \
--build-arg SERVICE_PORT=3002 \
--build-arg SERVICE_NAME=core \
-t natserract/mini_evv . && docker run --env-file .env -p 3002:3002 -t natserract/mini_evv

Limitations

  • Can't load Google Maps Iframe. Must enable billing to properly load a Google Maps iFrame. This is because the API requires an API key, which is created and managed through Google Cloud Console.

Next

  • Periodic schedules

About

Mini EVV Logger – Caregiver Shift Track

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors