Audience: System administrators at municipalities participating in the
Westmoreland County (WestMC) data exchange program.
Scope: Daily monitoring, quarterly operations, result interpretation.
CodeNForce maintains a mirror of selected Westmoreland County data tables in a
local westmc PostgreSQL schema. This data is never the system of record —
WestMC owns and publishes it through the AppSheets API. CodeNForce syncs from
that source on a schedule, and for two special datasets, a human operator reviews
and triggers the process manually each quarter.
There are three distinct processes. They have different scopes, different
risks, and different triggers.
| # | Process | Trigger | Scope | Touches Native Tables? | Cadence |
|---|---|---|---|---|---|
| 1 | Nightly Data Exchange | Automated (Quartz scheduler) | 7 mirror tables | No | Nightly |
| 2 | Tax Delinquency Mirror Refresh | Human operator | 1 mirror table | No | ~Quarterly |
| 3 | Parcel Data Harmonization | Human operator | Native parcel/address tables | YES | ~Quarterly |
Pulls new and updated records from the AppSheets API for seven supporting
data sets and writes them into the westmc.* mirror tables. This is a
read-from-remote, write-to-mirror-only operation. No CodeNForce native records
(properties, persons, cases) are modified.
| Table | Pull New | Pull Updates | Notes |
|---|---|---|---|
westmc_land_banking |
✓ | ✓ | Land banking status |
westmc_countywide_demo |
✓ | ✓ | County demolition data |
westmc_blight_record |
✓ | ✓ | Blight records pulled FROM WestMC into the CNF mirror. See note below. |
westmc_documents |
✓ | — | Document references |
westmc_parcel_photo |
✓ | — | Parcel photo references |
westmc_arpa |
✓ | ✓ | ARPA-funded project data |
westmc_demofund |
✓ | ✓ | Demo fund program data |
Not included in the nightly job:
westmc_parcel_data — handled by Process 3 (Parcel Harmonization)westmc_tax_delinquency — handled by Process 2 (Tax Delinquency Refresh)Blight record — dual-direction table:
westmc_blight_recordis included in
the nightly pull like other tables above. However, there is also a separate push
pathway: when an officer finalizes a property evaluation in CodeNForce, the resulting
blight record is pushed to WestMC through the AppSheets API. These officer-originated
records are not written back to thewestmc_blight_recordmirror table because
CodeNForce, not WestMC, is their system of record. The mirror table contains only
records that originated in WestMC and were pulled into CNF.
The Quartz scheduler triggers this automatically each night. The exact schedule
is configured in the application server. No manual action is required under
normal conditions.
SUCCESS entries in Recent Business OperationsIf the nightly job failed or you need to run it immediately:
Note: "Launch Data Exchange" runs the same table set as the nightly
Quartz job (the 7 tables above). It does not trigger Parcel Harmonization
or Tax Delinquency Refresh.
Pulls the current tax delinquency file from WestMC and refreshes the
westmc.westmc_tax_delinquency mirror table using an upsert strategy:
No CodeNForce native tables are touched.
TAX_DELINQUENCY, click Load Table DataThe growl message shows the number of records processed. No further action is
required — the updated data is available immediately for staff viewing.
This is the most consequential process in the WestMC integration.
It reads parcel records from westmc.westmc_parcel_data and attempts to match
each one to CodeNForce's native parcel records (humanparcel,
parcelmailingaddress). Where matches are found, it updates native address and
owner data. Records that cannot be matched are flagged for staff review.
This process modifies mission-critical native CodeNForce tables. Run it
deliberately, with appropriate staff awareness.
The results panel shows four metrics:
| Metric | Meaning |
|---|---|
| WestMC Records | Total parcel records read from the WestMC mirror for this municipality |
| Exact Matches | Parcels that matched an existing CodeNForce parcel record by tax map number. Native data was updated for these. |
| No Matches | Parcels with no corresponding CodeNForce native record. These are logged but no native record is created. |
| Flagged for Review | Parcels where a match was found but data conflicts were detected. A staff member must review and resolve these. |
Use Test tiny batch in the Muni Configuration table to run harmonization
against a small comma-separated list of specific parcel CNF IDs first. This
lets you verify the logic is working as expected before processing the full
municipality. Test results appear in a dialog and do not write to native tables.
The Data Exchange Dashboard is at:
Cogadmin → System Administration → Data Exchange Dashboard
Controls for global operations.
| Control | Purpose |
|---|---|
| View API Configuration | Inspect the AppSheets API credentials and environment settings. Does not modify anything. |
| Refresh Logging Data | Force a reload of the Recent Business Operations and API Calls tables in the Logging tab. |
| Launch Data Exchange | Manually runs the same 7-table nightly pull immediately for all municipalities. Equivalent to the nightly Quartz job. |
Per-municipality controls and the WestmcTableConfigEnum capability matrix.
Municipality table — Actions column:
| Button | Process | Notes |
|---|---|---|
| View Tables | Opens a dialog showing capability flags per table for this muni | Read-only reference |
| Harmonize Parcels | Process 3 | Modifies native tables. Run deliberately. |
| Test tiny batch | Process 3 (test only) | Dialog for testing against specific parcel IDs |
| Sync Tax Delinquency | Process 2 | Mirror only |
| Sync Now | Same as Launch Data Exchange, scoped to all municipalities | Currently equivalent to "Launch Data Exchange" — per-municipality scoping is a planned future enhancement |
WestMC Table Sync Settings panel:
Shows the current capability flags from WestmcTableConfigEnum.java for all
10 mirror tables. This is a developer reference showing what the code is
configured to do. If the "In Nightly Job" column shows NO for a table you
expect to be synced, a developer review is needed.
Developer and admin tools for verifying API connectivity and simulating the
data exchange with specific table selections. Test Mode limits which tables
are processed without touching nightly job configuration. Use this tab when
troubleshooting API failures or verifying a new municipality's configuration.
Logging data is loaded lazily. If the tables look empty or stale, click
Refresh Logging Data in the Exchange Tools tab.
Read-only paginated view of the raw mirror table data, sliced by municipality.
Currently supported: PARCEL_DATA and TAX_DELINQUENCY.
Use this to:
Each enum constant in WestmcTableConfigEnum.java configures how a single
WestMC table is handled. This table documents what each attribute does and
where it is consumed at runtime.
| Attribute | Method | Used by | What it controls |
|---|---|---|---|
cnfTableNameString |
getTableNameString() |
Coordinator (logging), Integrator (all SQL via getFullTableName() = "westmc." + this) |
The PostgreSQL table name in the westmc schema |
remoteTableNameString |
getRemoteTableNameString() |
WestmcAPIClient ("Table" field in every API request payload) |
The table name sent to the AppSheets API |
pullNewRecords |
canPullNewRecords() |
Coordinator (decides whether to call "fetch new records" path); supportsPull() = pullNew OR pullUpdated which gates nightly job inclusion |
Enable/disable pulling records that are new since last sync |
pullUpdatedRecords |
canPullUpdatedRecords() |
Coordinator (decides whether to call "fetch updated records" path) | Enable/disable pulling records modified since last sync |
syncRecords |
canSyncRecords() |
isBidirectional() guard in coordinator; displayed in Muni Config tab |
Enable bidirectional sync (pull AND push CNF data back to remote) |
pushNewRecordsOnly |
canPushNewRecordsOnly() |
isBidirectional() guard; displayed in Muni Config tab |
Enable push-only mode (send CNF-originated records to remote, no pull) |
batchSize |
getBatchSize() |
WestmcAPIClient: controls page size when fetching updated records from the API | Max records returned per API request; tune for API rate limits |
dtoClass |
getDtoClass() |
WestmcAPIClient: Jackson deserializes API JSON responses into this class | DTO class for JSON-to-Java mapping of records from the remote API |
lastUpdateFieldName |
getLastUpdateFieldName() |
WestmcAPIClient: name of the remote date field used to filter "records updated since X" | The AppSheets field name for the "last modified" timestamp on the remote table (varies: "last_updated_on", "last_updated", "last_update") |
remoteIdFieldName |
getRemoteIdFieldName() |
WestMCPostgresIntegrator: reflection-based extraction of remote ID from DTO object for deduplication | The Java field name on the DTO that holds the remote system's unique key |
psqlIdColumnName |
getPsqlIdColumnName() |
WestMCPostgresIntegrator: SQL SELECT ... WHERE <this> IN (...) for duplicate detection |
The PostgreSQL column name for the same remote ID |
remoteIdKeyName |
getRemoteIdKeyName() |
WestmcAPIClient: key name when sending ID mapping payloads back to AppSheets | The AppSheets API field name for CNF→remote ID mapping callbacks |
Blight record — two constants, one table: BLIGHT_RECORD_READ and BLIGHT_RECORD_WRITEONLY both reference westmc_blight_record but have opposite operation profiles. BLIGHT_RECORD_READ has pull flags set and is included in the nightly job. BLIGHT_RECORD_WRITEONLY has pull flags unset (supportsPull() = false) and is the configuration used when CNF pushes officer-created blight records to WestMC on property evaluation finalization — it is excluded from the nightly job.
Attributes confirmed unused in current business processes:
isBidirectional() is checked once as a guard but no table currently passes the bidirectional sync path end-to-endgetRemoteEndpoint() is @Deprecated and has no callers| Item | Current Behavior | Planned |
|---|---|---|
| Gap 2 — temporal columns | validfrom/validto added to humanparcel and parcelmailingaddress in DB but not yet populated by Java code |
Wire in Integrator/Coordinator |
| Gap 4 — conflict queue | Flagged-for-review parcels accumulate with no assignment or SLA | Assignment model |
| Gap 7 — zombie reactivation logging | Tax delinquency upsert silently reactivates records; no specific log event | Add TAX_DELINQUENCY_REACTIVATED log type |
Last updated: July 2026. Maintained in this wiki (admin/westmc-data-exchange.md). For developer architecture notes see Data exchange in Postgres.