This tracker is designed so each project lead can set up attendance for their own project with:
- one Google Sheet
- one Google Apps Script web app bound to that sheet
- one deployed copy of
index.html
The system uses a sign-in / sign-out event model:
- the frontend records scans locally first
- the frontend keeps the current roster in browser storage so refreshes do not lose state
- the frontend syncs events to Google Sheets in the background
- the backend stores raw events in
Events - the backend finalizes attendance from
Eventsafter a session has ended - the backend appends finalized rows into
Attendanceonce per session/date
If you are setting this up for a new project, do these 8 things:
- Create a Google Sheet for your project.
- Open Apps Script from that sheet and paste in
Code.gs. - Run
setupSpreadsheet(). - Fill in the
Configtab with your project name, password, and session times. - Deploy the Apps Script as a web app.
- Open
index.htmland replaceYOUR_APPS_SCRIPT_WEB_APP_URLwith your own deployed Apps Script URL. - Push the frontend to GitHub Pages or host it somewhere similar.
- Open the page, enter the password from your
Configtab, and test one sign-in.
If you only remember one thing:
- every project needs its own Google Sheet backend
- every deployed frontend must point at that project’s own Apps Script
apiUrl
Code.gs- paste this into Apps Script
- optionally edit backend
SETTINGS
index.html- set the Apps Script
apiUrlto your own deployed backend - deploy this through GitHub Pages
- set the Apps Script
Manual entry accepts:
12345678123456A
Barcode scans also accept a 3-character prefix:
xxx12345678xxx123456A
The first 3 characters are ignored when present.
To set up a new project:
- Create a new Google Sheet for that project.
- Add the backend code in Apps Script.
- Run
setupSpreadsheet(). - Fill in the
Configtab. - Deploy the Apps Script as a web app.
- Put that web app URL into
index.html. - Push the frontend to GitHub Pages.
You can follow the detailed steps below if this is your first time doing it.
Create one Google Sheet per project.
Example:
Drone Team Attendance
- Open the Google Sheet.
- Go to
Extensions->Apps Script. - Delete the sample code.
- Paste in the contents of
Code.gs. - Save the script.
In Apps Script:
- Select
setupSpreadsheet - Click
Run - Approve the script if prompted
This creates the required tabs:
ConfigEventsAttendance
The Config tab must use this exact header row:
Project Name | Password | Session Name | Day | Start Time | End Time | Active
Example:
Drone Team | CR0C | Drone Team Build Session | Tuesday | 16:30 | 21:00 | TRUE
Drone Team | CR0C | CRoC Build Night | Thursday | 17:40 | 21:00 | TRUE
Column meanings:
Project Name: shown in the page title and headingPassword: required to unlock the frontendSession Name: label for that sessionDay: full weekday name such asTuesdayStart Time:HH:MMEnd Time:HH:MMActive:TRUEto enable the session row
Use the same Project Name and Password on each active row for that project sheet.
The backend operational settings are near the top of Code.gs:
var SETTINGS = {
duplicateCooldownSeconds: 10,
standardizedFallbackAttendanceHours: 1,
finalizationGraceSeconds: 30,
recentLimit: 12,
syncBatchSize: 10,
};What they control:
duplicateCooldownSeconds: short cooldown that prevents an immediate second scan from accidentally signing someone outstandardizedFallbackAttendanceHours: fallback attendance used for an unfinished final interval at session endfinalizationGraceSeconds: short delay after session end before attendance is finalized, to allow late syncs to arriverecentLimit: how many recent events the frontend showssyncBatchSize: how many queued events are sent to the backend at once
The default values are sufficient for normal use. You usually do not need to change them.
If you do want to adjust them:
- Edit the
SETTINGSobject inCode.gs - Save the Apps Script project
- Redeploy the web app as a new version
After saving the script:
- Click
Deploy->New deployment - Choose
Web app - Set:
Execute as:MeWho has access:Anyone
- Click
Deploy - Copy the web app URL
For project admins:
- use
Execute as: Me - use
Who has access: Anyone - these settings let the public frontend call the Apps Script backend correctly
If you change the backend later:
- Save the Apps Script code
- Open
Deploy->Manage deployments - Edit the web app deployment
- Select
New version - Deploy again
The URL usually stays the same, but the deployment version must be updated.
Open index.html and set the backend URL in the CONFIG constant:
const CONFIG = {
apiUrl: "YOUR_APPS_SCRIPT_WEB_APP_URL",
storagePrefix: "croc-attendance-v3",
syncRetryMs: 4000,
pollIntervalMs: 15000
};Replace apiUrl with your deployed Apps Script web app URL.
Important:
- do not leave the placeholder value in place
- do not point two different projects at the same backend sheet unless you intentionally want them sharing attendance data
- if you copy this repo for a new project, changing
apiUrlis the first frontend change you should make
The frontend does not store the password. Users enter it on the page, and the backend verifies it against the Config tab.
The frontend is a single file:
If GitHub Pages is already configured:
- Update
index.html - Commit the change
- Push to GitHub
- Wait for GitHub Pages to redeploy
For most projects, you only need to change two things:
- The
Configtab in the project’s Google Sheet - The
apiUrlinindex.html
You usually do not need to change anything else.
Used for project name, password, and session schedule.
The Events tab is append-only.
Header:
Timestamp | Date | Member ID | Event Type | Method | Project Name | Session Name | Session Times | Day | Event ID | Device ID | Session Key
You should not normally edit this tab manually.
The Attendance tab is finalized from Events after a session has ended.
Header:
Date | Member ID | Project Name | Session Name | Session Times | Day | Sign In | Sign Out | Attendance Hours | Notes
Notes:
Attendance Hoursis calculated as a decimal rounded to 2 decimal places- multiple sign-in / sign-out pairs in one session are summed
- if someone signs in and never signs out, and the session has ended, the unfinished final interval uses the backend fallback setting
- if the operator uses
End Session (Sign Out All), every remaining signed-in member is signed out with the same fallback setting Notesstays blank for normal paired sign-in / sign-out attendanceNotesis filled automatically only when fallback time is used- fallback notes record whether the fallback came from
End Session (Sign Out All)or from a missing sign-out before session end, plus the relevant event time - once attendance rows for a session have been appended, they are not rebuilt or overwritten later
You can edit past attendance rows manually after they have been imported. Future sessions append new rows only.
A session is open when:
Daymatches today- current time is within
Start Time <= now < End Time
If no session is open:
- the page shows that no session is open
- the next configured session is shown when available
- sign-ins are blocked
The frontend:
- keeps local session state in
localStorage - restores that state after refresh
- polls the backend periodically so multiple open pages stay reasonably in sync
- shows the current session
- shows a live
Currently Signed Inroster - shows recent sign-in / sign-out activity
- includes an
End Session (Sign Out All)button underRecent Activity - starts the camera automatically after a successful unlock when a session is open
Normal scan flow:
- first scan for a member signs them in
- a quick repeat scan during the cooldown window does not sign them out
- a later scan after the cooldown window signs them out
- members can sign in and out multiple times in the same session, and attendance is summed
End-of-session flow:
- if members are still signed in, use
End Session (Sign Out All) - this creates forced sign-out events for everyone still on the roster
- those forced sign-outs use the same backend-configured fallback interval as members who never signed out before session end
- pressing
End Session (Sign Out All)does not close the session early; students can still sign in again until the scheduledEnd Time - once the configured finish time has passed and the
finalizationGraceSecondsdelay has elapsed, the backend finalizes that session fromEvents - finalization appends one attendance row per member for that session/date
- late syncs for an already finalized session are ignored
Use a wide test session first:
Project Name | Password | Session Name | Day | Start Time | End Time | Active
Drone Team | CR0C | Test | Friday | 01:00 | 20:00 | TRUE
Then test:
- Open the frontend page.
- Enter the password.
- Confirm the title becomes
Drone Team Attendance. - Scan or enter one member ID.
- Confirm they appear in
Currently Signed In. - Scan the same member again immediately and confirm they stay signed in.
- Scan the same member again after the duplicate cooldown window and confirm they sign out.
- Sign in one member and use
End Session (Sign Out All). - Confirm they are removed from
Currently Signed In. - Refresh the page and confirm the roster restores correctly.
- Confirm the events appear in
Events. - After the session finish time passes, confirm attendance rows are appended to
Attendance.
Usually one of these:
- the frontend
apiUrlpoints to the wrong Apps Script deployment - the Apps Script code was saved but not redeployed as a new version
- the page password does not match the
Passwordcolumn inConfig
Use HH:MM values in Config, for example:
01:0016:3020:00
Check the first non-empty Project Name value in the Config tab.
Check:
- the Apps Script deployment is current
- the frontend is using the correct
apiUrl - the session is currently open
Attendance is only appended after the configured session end time has passed and the short finalization grace period has elapsed. Before that, only Events updates.
That is expected if they were left signed in until session end or signed out via End Session (Sign Out All). In those cases the backend uses standardizedFallbackAttendanceHours for the unfinished final interval instead of calculating the full elapsed time.
That means fallback time was used instead of a normal paired sign-in / sign-out duration. The note tells you whether it came from End Session (Sign Out All) or from a missing sign-out before the session was finalized.
That is the current design. Attendance rows are appended once when a session is finalized. Later sessions append new rows only, and previously imported attendance rows are not rebuilt.
The page restores local state immediately, then syncs and polls the backend. If another device just scanned, wait for the next poll or refresh the page.