Gazetteer Place Church Register - FreeUKGen/MyopicVicar GitHub Wiki
Part of FreeREG-Core-Records.
Models: app/models/place.rb, church.rb, register.rb, place_cache.rb, alternateplacename.rb, alternatechurchname.rb ยท Controllers: places_controller.rb, churches_controller.rb, registers_controller.rb, place_caches_controller.rb
Every FreeREG record hangs off a three-level location hierarchy: a Place (a parish or settlement within a county) has many Churches, and each church has many Registers (one per kind of source document: parish register, bishop's transcripts, etc.). Batches of transcribed records (Freereg1CsvFile) belong to a register.
Place (SOM, "Wells")
โโ Church ("St Cuthbert")
โโ Register (PR) โโ Freereg1CsvFile โโ Freereg1CsvEntry โโ SearchRecord
โโ Register (BT) โโ ...
Coordinators maintain this hierarchy by hand through the county management screens (Coordinator-Admin-Tools). The CSV processor does not create places or churches. A line whose place or church isn't already in the gazetteer is rejected (see CSV-Processing-Pipeline). The processor only creates registers.
Identity: chapman_code + place_name. There's no unique index on the pair. Duplicates are possible, and merge_places exists to clean them up.
Key fields:
-
place_name, plusmodified_place_name: lower-cased with-,.and'stripped. The CSV processor matches onmodified_place_name, so"St. Mary-le-Bow"in a file matches"St Mary le Bow"in the gazetteer. -
grid_referenceorlatitude/longitude: one is required (grid_reference_or_lat_lon_present_and_valid). For FreeREG, lat must be 45โ70 and lon โ10โ5.before_save :add_location_if_not_presentderiveslocation([lon, lat], 2dsphere-indexed) from whichever is present.locationdrives the "search nearby places" radius search (Search-Query-System). -
disabled: a String,"true"/"false", not a Boolean. Always compare against the string. -
error_flag:"Place name is not approved"marks a place a coordinator hasn't approved yet.Place.approvedexcludes these.PlacesController#approveclears it and recomputesmodified_place_name. -
data_present: Boolean, true when any register under it has batches. Places withdata_present: falsedon't appear in the search form's place picker (see PlaceCache below). -
original_*fields: a one-time snapshot taken bysave_to_originalbefore the first rename or relocation. It's written only once (if original_chapman_code.nil?), so it records the first state, not the previous one. - Denormalised counters:
records,datemin,datemax,daterange,transcribers,last_amended. See FreeREG-Content-and-Statistics. -
ucf_list/ucf_list_record_count/ucf_list_file_count: a per-place index of search records containing UCF wildcards, keyed byFreereg1CsvFileid. See Search-Record-Indexing. -
genuki_url: link to the GENUKI page for the place (renamed and made clickable in Sept 2026). - FreeCEN-only:
cen_data_years, and thefreecen_pieces/freecen_dwellingsrelations.
Callbacks: after_save :update_places_cache rebuilds the whole county's PlaceCache on every place save, not only saves that change the name. Bulk scripts that touch many places in one county will rebuild that cache once per place.
Deleting a place is a soft disable. PlacesController#destroy sets disabled: 'true', data_present: false, and refuses if the place is unapproved, already disabled, or still has churches or search records. has_many :churches, dependent: :restrict_with_error backs this up at the model level.
Key fields: church_name, denomination, place_name (denormalised copy of the parent place's name), church_notes, website, location, location_of_source_documents (added Aug 2026, #2903), plus the same counter fields as Place. Embeds alternatechurchnames.
Name standardisation: Church.standardize_church_name expands abbreviations and normalises saint names using FreeregOptionsConstants::CHURCH_WORD_EXPANSIONS / COMMON_WORD_EXPANSIONS (Andrews โ Andrew, Bartholemew โ Bartholomew, โฆ). It runs on rename and on every church name the CSV processor reads, so the stored name can differ from what the transcriber typed.
Church#my_registers returns registers in the display order given by FreeregOptionsConstants::REGISTER_TYPE_ORDER.
Key fields: register_type (see lib/register_type.rb), register_name (legacy, mostly nil), alternate_register_name (the one the processor actually matches on, built as "#{church_name} #{register_type}"), status, quality, source, copyright, register_notes, minimum_year_for_register/maximum_year_for_register, credit, plus counters.
Relationships: has_many :freereg1_csv_files, :sources (both restrict_with_error), :embargo_rules, :gaps. The sources relation is where the Image-Server-System attaches: Register#add_source creates the single Source named "Image Server" for a register. Register.image_transcriptions_calculation summarises image statuses per source/group for the register page.
Register.update_or_create_register(file) is called from Freereg1CsvFile#update_register when the processor creates a new batch:
-
find_register: finds the church by chapman code + place name + church name, then a register on it whosealternate_register_name(falling back toregister_name) equals"<church> <type>". - If none is found,
create_register_for_churchcreates it. That method also has branches to create a missing Place (with placeholder grid refTQ336805and the "not approved" flag) and a missing Church. The processor validates both before this point, so in practice those branches only run from other callers. - Attaches the file to the user (
UseridDetail#freereg1_csv_files) and setsdata_presenton the place, refreshing the county PlaceCache if the place wasn't in it yet.
find_register passes the church to create_register_for_church through the class variable @@my_church. That works because the processor is single-threaded per process. Don't call these two methods separately or from concurrent code.
Unspecified vs Unknown: historically, a CSV church field without a register-type suffix produced register type ' ' ("Unspecified"). Since Sept 2026 (6045c2f1f) the processor records it as UK ("Unknown") and warns the transcriber once per church. Old data still has ' ' registers. lib/tasks/set_unspecified_register_type_to_unknown.rake migrates them.
PlaceCache is one document per county holding places_json, a JSON map of place_id โ "Place Name (County Name)" for every place in that county with data_present: true and disabled: "false". The search form's place picker loads it (PlacesController#for_search_form).
-
PlaceCache.refresh(chapman_code)deletes and rebuilds one county. -
PlaceCache.refresh_cache(place)rebuilds only if the place's name is missing from the cached JSON. It's used after renames and relocations. -
PlaceCache.refresh_all/check_and_refresh_if_absentare for rake/console use.
If a place with data doesn't show up in the search form, a stale PlaceCache is the first thing to check.
All of these are coordinator actions on the place/church/register pages. They use hand-written bulk updates against search_records and freereg1_csv_entries, and none run in a transaction.
| Operation | Method | What it rewrites |
|---|---|---|
| Rename place | Place#change_name |
Place name โ Freereg1CsvEntry.place, Freereg1CsvFile.place, Church.place_name, SearchRecord.location_names[0]; locks every batch under the place (propogate_batch_lock) |
| Move place to another county | Place#relocate_place |
chapman_code on all search records for the place, county on all entries and batches |
| Merge duplicate places | Place#merge_places |
Moves churches and search records from the other same-named places in the county onto this one, then hard-deletes them. Refuses if another copy has notes or alternate names (has_input?) or if self is the unapproved one |
| Rename church | Church#change_name |
Standardises the name, then entries, batches and SearchRecord.location_names[0]
|
| Move church to another place | Church#relocate_church |
Iterates every entry of every batch, rewriting its search record's location_names, place_id, chapman_code, and the entry's county/place; then recalculates counters on both places |
| Merge churches | Church#merge_churches |
Moves registers from same-named churches in the place onto this one; refuses if the other copy has input. The losing church is detached, not deleted (see note below) |
| Change register type | Register#change_type |
alternate_register_name, batch and entry register_type, and search record location_names
|
| Merge registers | Register#merge_registers |
Moves batches from same-type registers on the church onto this one; refuses if the other copy has input. Only batches move. The losing register is detached, not deleted, and keeps its sources, embargo_rules and gaps
|
"Detached, not deleted": merge_churches and merge_registers remove the loser with place.churches.delete(church) / church.registers.delete(register). Both associations are dependent: :restrict_with_error, so Mongoid (8.1) doesn't delete the document. It sets the foreign key to nil and saves it. The result is an orphan Church with place_id: nil, or an orphan Register with church_id: nil, which no page links to. The extract_register_orphans / delete_register_orphans rake tasks exist to clean these up. By contrast, merge_places and Freereg1CsvFile#merge_batches really do call .delete.
resources :places, :churches, :registers, :place_caches, plus member-style routes for rename, relocate, merge, approve (places), create_image_server/show_image_server (registers), and the JSON endpoints places/for_search_form, places/for_freereg_content_form.