#!/usr/bin/env bash set -euo pipefail # Poll ServiceM8 form responses, parse Quote Template responses, then apply any # newly/unapplied parsed quote responses to ServiceM8 jobMaterials. # # Default window: last 24 hours from script start, in local system time. # Override with: # --since 'YYYY-MM-DD HH:MM:SS' # --hours 48 # # By default this wrapper applies unapplied parsed responses. Use --dry-run to # run the poll and preview each pending apply without writing to ServiceM8. # The apply script still refuses duplicate applies unless --force is explicitly # passed through. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" POLL_SCRIPT="$SCRIPT_DIR/poll_form_responses_since.py" APPLY_SCRIPT="$SCRIPT_DIR/apply_polled_quote_template_jobmaterials.py" DB_PATH="${WEBHOOK_POLL_DB_PATH:-$SCRIPT_DIR/servicem8_formresponse_poll.db}" LOG_DIR="${WEBHOOK_RUN_LOG_DIR:-$SCRIPT_DIR/logs}" QUOTE_TEMPLATE_FORM_UUID="${SERVICEM8_QUOTE_TEMPLATE_FORM_UUID:-3621b6be-1d19-4756-9ab4-9d5e4120f6d9}" SINCE="" HOURS="24" FORCE="0" DRY_RUN="0" usage() { cat <&2 usage >&2 exit 2 ;; esac done if [[ -z "$SINCE" ]]; then SINCE="$(date -d "-${HOURS} hours" '+%Y-%m-%d %H:%M:%S')" fi mkdir -p "$LOG_DIR" RUN_ID="$(date '+%Y%m%d-%H%M%S')" LOG_FILE="$LOG_DIR/poll-and-apply-$RUN_ID.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "== ServiceM8 Quote Template poll/apply run ==" echo "Started: $(date --iso-8601=seconds)" echo "Since: $SINCE" echo "Mode: $([[ "$DRY_RUN" == "1" ]] && echo "dry-run" || echo "apply")" echo "DB: $DB_PATH" echo "Log: $LOG_FILE" echo echo "== Polling form responses ==" "$POLL_SCRIPT" --since "$SINCE" --summary-limit 20 echo echo "== Finding unapplied parsed Quote Template responses ==" mapfile -t FORM_RESPONSE_UUIDS < <( python3 - "$DB_PATH" "$QUOTE_TEMPLATE_FORM_UUID" <<'PY' import sqlite3 import sys poll_db, quote_form_uuid = sys.argv[1], sys.argv[2] conn = sqlite3.connect(poll_db) conn.row_factory = sqlite3.Row rows = conn.execute( """ select q.form_response_uuid from quote_template_form_responses q left join form_responses_raw r on r.uuid = q.form_response_uuid where q.form_uuid = ? and coalesce(q.process_status, '') != 'applied' order by coalesce(r.timestamp, q.discovered_at) asc, q.form_response_uuid asc """, (quote_form_uuid,), ).fetchall() for row in rows: print(row["form_response_uuid"]) PY ) if [[ ${#FORM_RESPONSE_UUIDS[@]} -eq 0 ]]; then echo "No unapplied Quote Template responses found." echo "Finished: $(date --iso-8601=seconds)" exit 0 fi printf 'Found %d unapplied Quote Template response(s):\n' "${#FORM_RESPONSE_UUIDS[@]}" printf ' - %s\n' "${FORM_RESPONSE_UUIDS[@]}" echo if [[ "$DRY_RUN" == "1" ]]; then echo "== Dry-run preview only; no ServiceM8 writes ==" APPLY_ARGS=(--pretty) else echo "== Applying to ServiceM8 ==" APPLY_ARGS=(--apply --pretty) fi if [[ "$FORCE" == "1" ]]; then APPLY_ARGS+=(--force) fi for uuid in "${FORM_RESPONSE_UUIDS[@]}"; do echo if [[ "$DRY_RUN" == "1" ]]; then echo "-- Dry-run form_response_uuid=$uuid --" else echo "-- Applying form_response_uuid=$uuid --" fi "$APPLY_SCRIPT" --uuid "$uuid" "${APPLY_ARGS[@]}" done echo echo "Finished: $(date --iso-8601=seconds)" echo "Log: $LOG_FILE"