Batch Records Freereg1CsvFile and Entry - FreeUKGen/MyopicVicar GitHub Wiki
Part of FreeREG-Core-Records.
Models: app/models/freereg1_csv_file.rb, freereg1_csv_entry.rb, batch_error.rb, multiple_witness.rb, physical_file.rb, attic_file.rb Β· Controllers: freereg1_csv_files_controller.rb, freereg1_csv_entries_controller.rb, physical_files_controller.rb
These are the persistent records of a transcription. The upload-time Csvfile object is discarded at the end of the request (Csvfile-System). After that, the data lives in these documents plus the file on disk.
| Record | Granularity | What it's for |
|---|---|---|
PhysicalFile |
one per userid + file_name
|
Tracks the file on disk (Rails.application.config.datafiles/<userid>/<file_name>): uploaded / waiting to be processed / processed. It's also the processor's queue (PhysicalFile.waiting) |
Freereg1CsvFile |
one per userid + file_name + location + record type |
A batch: the records from one file for one county/place/church/register type. Holds header metadata, counters, lock flags, error count |
Freereg1CsvEntry |
one per data line | A single baptism, marriage or burial as transcribed |
One uploaded file normally produces one batch. A file whose lines span several churches or register types produces several, and the processor warns the transcriber when that happens (CSV-Processing-Pipeline).
Location fields: county (actually a Chapman code), chapman_code, place, place_name, church_name, register_type, record_type. The comment in the model says the location fields are "NOT USED" in favour of the Register β Church β Place chain. In practice both are used: the processor looks batches up by these strings (setup_batch_for_processing), and the display uses the chain. Relocation code keeps both in sync.
Header metadata: transcriber_name/_email/_syndicate, credit_name/_email, first_comment/second_comment, transcription_date, modification_date (both strings like "12 Mar 2019", defaulting to '01 Jan 1998'), uploaded_date, lds ('yes'/'no': file includes film/film_number columns), def (Boolean: flexible column order) and order (the column β index map used to write the file back out), characterset, digest (MD5 of the file on disk), software_version, search_record_version.
Counters: records (String), datemin, datemax, daterange (Array of per-decade counts), zero_entries (Boolean: some entries have no year), error (Integer: count of BatchErrors).
State flags:
-
processed/processed_date: true once search records have been created. A trainee's "check only" run leaves itfalse. -
locked_by_transcriber/locked_by_coordinator: see Locking below.
Callbacks:
-
before_save:add_lower_case_userid_to_file,add_country_to_file,check_register_type(adds an error ifregister_typeisn't inRegisterType::OPTIONS, but because it runs inbefore_saverather than as a validation, Mongoid still saves the document; the error only shows up if the caller inspectserrorsafterwards). -
after_save:recalculate_last_amended(walks the whole place's churches, registers and files) andupdate_number_of_files(recounts all of the user's files and records). Every save of a batch triggers both. That adds up in loops. -
before_destroy: moves the disk file to the attic anddestroy_alls the entries, which in turn destroy their search records.remove_batchdeliberately callsdelete, notdestroy, to skip this (see Removal below).
Colour coding (define_colour, used in batch listings): red = errors & unlocked, orange = not processed, green = clean & unlocked, blue = clean & locked, maroon = errors & locked.
include Mongoid::Attributes::Dynamic, so an entry can carry any field. The declared fields cover the standard and extended layouts for all three record types (see the field lists in FreeregOptionsConstants: ENTRY_ORDER_DEFINITION for the classic layout, ADDITIONAL_*_FIELDS / EXTENDED_*_LAYOUT for the flexible one).
Common fields: county, place, church_name, register_type, record_type, register_entry_number, notes, notes_from_transcriber, image_file_name, file_line_number, line_id ("<userid>.<FILE_NAME>.<line>"), year, film, film_number, record_digest.
Relationships:
belongs_to :freereg1_csv_file-
embeds_many :multiple_witnesses(up toMAXIMUM_WINESSES = 8). The processor turnswitnessN_forename/_surnamecolumns into these for marriages and baptisms. -
embeds_many :embargo_records: history of embargo decisions for this entry; the most recently updated one is the current state. See Embargoes-and-Gaps. -
has_one :search_record, dependent: :restrict_with_error, but alsobefore_destroy { SearchRecord.destroy_all(freereg1_csv_entry_id: id) }, which removes the search record first so the restriction never fires.
Callbacks:
-
before_save:sanitize_fields,add_digest(MD5 of a type-specific concatenation of fields βrecord_digest),captitalize_surnames(every*_surnameis upcased on save),check_register_type. -
after_update :update_seach_record_locationβSearchRecord#update_location: rewriteslocation_names/place_idfrom the batch's current register chain.
Validation: errors_in_fields runs the FreeregValidations checks (clean text, valid dates, valid ages, sex, etc.) per record type. On a CSV line, a failure becomes a BatchError instead of an entry. In the online editor it's a normal form error.
record_digest drives re-upload reconciliation. The processor matches incoming lines to existing entries by digest, not by line number. Any change to cal_digest/create_*_string makes every existing entry look new on its next re-upload, which leads to a full delete-and-recreate of its search records.
enough_name_fields? decides whether an entry gets a search record at all. For example, a baptism needs at least one of person forename, father forename/surname, mother forename/surname. Entries that fail this exist but are unsearchable.
A line that failed validation during processing: error_type, record_number, error_message, record_type, and data_line (the parsed hash, so the fix-it form can be pre-filled). Transcribers correct these online through Freereg1CsvEntriesController#create with session[:error_id] set. That path reuses the error's line number and line id instead of appending a new line.
Two independent Booleans, set on every batch sharing the file's userid + file_name:
-
locked_by_transcriberis set when the owner edits online (session[:my_own]) or toggles the lock. -
locked_by_coordinatoris set when a coordinator edits, renames or relocates the place above it (Place#propogate_batch_lock), merges batches, or toggles the lock.
A locked batch can't be replaced by re-upload (Csvfile#setup_batch_on_replace) or removed. The reason: the database now differs from the file on disk, and a re-upload would silently throw away the online edits. Downloading clears both locks. Freereg1CsvFilesController#download first regenerates the CSV from the database (backup_file β write_csv_file, previous version moved to the attic), updates digest, then calls force_unlock.
Since Sept 2026 (01594b4cf), an embargo-only edit of an entry doesn't back up or lock the file, because it doesn't change the CSV.
update, create and destroy all follow the same shape:
- Validate the entry β file link (
valid_freereg1_csv_entry?/valid_freereg1_csv_file?). - Refuse if the file is waiting in the processor queue (
can_we_edit?β noPhysicalFile.waitingfor it). - Save the entry. For DEF files,
check_and_augment_defadds any newly used column to the file'sorderso a download includes it. -
update_file_statistics:SearchRecord.update_create_search_record(re-applies embargo rules, re-translates, re-indexes), thencalculate_distribution,backup_file(rewrites the CSV on disk),lock_all, setmodification_date. - Update the place's UCF list for this record (
update_place_ucf_list).
create also calls update_other_statistics and destroy calls update_statistics_and_access; both recalculate the Register/Church/Place counters. update doesn't (the update_statistics_and_access call is commented out), so after an edit that changes a date, the contents pages lag until the next freereg:calculate_freereg_content or re-upload.
-
lock: toggles the transcriber lock (owner) or the coordinator lock (coordinator; clearing the coordinator lock also clears the transcriber lock). -
download: see Locking. Refused bycheck_fileif any batch of the file has no register/church/place, no entries, or anyerror > 0. Error lines exist only asBatchErrors, so they'd be lost from the regenerated file. -
remove:remove_batch(max_records). Refused if the batch is locked, or if it has more records than the user's role allows (MAX_RECORDS_COORDINATOR = 5000,MAX_RECORDS_DATA_MANAGER = 15000,MAX_RECORDS_SYSTEM_ADMINISTRATOR = 100000); over the limit, the data manager gets an email instead. Otherwise it:- appends
id,userid,file_nameto the delete list (config.delete_list), - moves the disk file to the attic,
- removes the batch from the place's UCF list,
-
deletes (notdestroys) the batch document, and - deletes the
PhysicalFile.
The entries and search records stay in the database until the
delete_file_no_sleeprake task works through the delete list. Until then they're orphans. They don't show in results, becauseSearchQuery#persist_resultswalks each hit's entry β batch β register β church β place chain (valid_freereg_entry_ids_for_result_hashes), deletes any search record whose chain is broken, and drops it from the results. They do still count against the 500-result cap, though, and a search can return fewer rows than it matched. See FreeREG-Operations-and-Rake-Tasks. - appends
-
merge:merge_batchesmoves the entries of other batches with the same userid + file_name on the same register into this one and deletes them. It adds torecords; the batch's "refresh file information" action (refresh_file_information) recalculates the date range afterwards. -
relocate: moves a batch to a different place/church/register (file_update_location), then rewrites every entry's search record location (propogate_file_location_change). -
change_userid:change_owner_of_file. Moves the disk file into the new user's folder, updates thePhysicalFile, and rewritesuseridon every batch and entry of the file. -
zero_year/show_zero_startyear_entries: list entries with no extractable year. These have nosearch_date, so they only turn up in searches without a date range. -
unique_names: distinct surnames/forenames in the batch. -
Listing variants (
my_own,display_my_error_files,display_my_own_files_waiting_to_be_processed, sorted by upload dateβ¦) are the transcriber's "My batches" views. Coordinators get the equivalent per county viaManageCountiesController(Coordinator-Admin-Tools) and per syndicate viaManageSyndicatesController(Syndicate-System).
Before a disk file is overwritten or removed, save_to_attic renames it to <file_name>.<unix_time>, moves it to <datafiles>/<userid>/.attic/, and creates an AtticFile record. Nothing prunes the attic automatically.