LLImporter
Imports the data of an external workbook into the linelist that holds this object. CheckImportFile reads the file and says whether it can be imported, HasSameLanguage compares the two interface languages, and CurrentLanguage answers the language of this linelist. ImportData brings the HList and VList data in, ImportCustomDropdown the custom dropdowns, ImportShowHide the show/hide choices, ImportEditableLabels the labels a user may edit, ImportSingleValues the single metadata values and ImportGeobase the geobase. ClearData empties the linelist first when the caller asks for it, and HasData says whether the linelist already carries rows.
THE REPORT
CompareWithImportFile records which sheets and which variables the two files have in common and which they have on their own. NeedReport says whether anything was recorded, FinalizeReport writes the entries into ImportReport, ReportSheets and ReportVariables read them back, and ClearReport empties them. Entries filed along the way leave through HasCheckings and CheckingValues.
Depends on: LLdictionary, Passwords, CustomTable, DataSheet, LLGeo, LLChoices, LLVariables, DropdownLists, HiddenNames, ShowHide, ShowHideLayout, ShowHideStore, ChoiceKeys, ImportMetadata, Checking, BetterArray, ImportReport
Factory
Create #
create
Create an importer from the source linelist workbook
Signature:
Public Function Create(ByVal sourceWkb As Workbook) As LLImporter
Factory method that validates the workbook is not Nothing, then constructs and returns a new LLImporter instance. Called on the predeclared instance.
Parameters:
sourceWkb: Workbook. The linelist workbook that will receive imported data.
Returns: LLImporter. A fully initialised instance ready for import operations.
Throws:
- vbObjectError When the workbook is Nothing.
Diagnostics
HasCheckings #
has-checkings
Whether the import filed anything worth reading
Signature:
Public Property Get HasCheckings() As Boolean
What the import found and what it decided to skip. Every entry is in English and the worksheet the caller writes them to is what the user reads.
Returns: Boolean. True when at least one entry was filed.
CheckingValues #
checking-values
The entries the import filed
Signature:
Public Property Get CheckingValues() As Checking
Returns: Checking. The store, ready to hand to a CheckingOutput.
Data Checks
HasData #
has-data
Whether the workbook contains user-entered data
Signature:
Public Property Get HasData() As Boolean
Scans all HList sheets for rows carrying more filled cells than an untouched row does. LLDataEntry records that figure per sheet under the hidden name blank_row_count when it makes the table. Returns True as soon as a single row has user data. Used to warn the user before clearing or overwriting.
Returns: Boolean. True when at least one HList row has user data.
CurrentLanguage #
current-language
The language this linelist was built in
Signature:
Public Property Get CurrentLanguage() As String
The workbook-level RNG_DictionaryLanguage hidden name, which the build set to
the dictionary language. The Metadata sheet of an export carries the same fact
under language, so the two compare like with like.
Returns: String. The language code, empty when the workbook carries none.
HasSameLanguage #
has-same-language
Check whether the file being imported is in this linelist's language
Signature:
Public Function HasSameLanguage(ByVal meta As ImportMetadata) As Boolean
Compares the language tag of the file with CurrentLanguage. A file naming no
language answers False, which is also the answer for a file carrying no
Metadata sheet at all. The caller tells those two apart through the metadata
object and says which happened.
The answer used to be kept on the class and read by the three metadata imports, so a caller that skipped this step got False by default and all three did nothing with no diagnostic. The three take the metadata object now and the caller decides whether to call them.
Parameters:
meta: ImportMetadata. The metadata of the file being imported.
Returns: Boolean. True when the languages match.
CheckImportFile #
check-import-file
Read the file over before importing it, and file what is wrong
Signature:
Public Function CheckImportFile(ByVal impwb As Workbook, _
ByVal meta As ImportMetadata) As Boolean
Every worksheet an export is supposed to carry is looked for, and the export options are read. The dictionary and the choices are errors when they are absent, because the labels and the category lists cannot be compared without them; the other metadata sheets are warnings, because the data still lands.
ONE THING STOPS AN IMPORT
A custom export written with the labels as its header row carries no variable name anywhere, so no column of it can be matched to a column of this linelist. Every row of such a file used to land in "not imported" with nothing said. It is refused here, with the reason.
Parameters:
impwb: Workbook. The opened import workbook.meta: ImportMetadata. What that workbook says about itself.
Returns: Boolean. True when the import may go on.
Data Operations
ClearData #
clear-data
Clear all user-entered data from HList and VList sheets
Signature:
Public Sub ClearData()
Iterates over all worksheets in the source workbook. For HList sheets, clears non-formula column values from the ListObject data body. For VList sheets, clears non-formula named range values. Unprotects and re-protects each sheet via Passwords.
ImportData #
import-data
Import data from HList and VList worksheets
Signature:
Public Sub ImportData(ByVal impwb As Workbook, ByVal pasteAtBottom As Boolean, _
ByVal meta As ImportMetadata)
Iterates over the import workbook worksheets, matching them to source workbook sheets by name. For HList sheets, delegates to ImportHListSheet. For VList sheets, delegates to ImportVListSheet. Tracks imported sheets and variables, and builds "not imported" report entries for sheets and variables not found in the current workbook.
Parameters:
impwb: Workbook. The opened import workbook.pasteAtBottom: Boolean. When True, appends data below existing rows.meta: ImportMetadata. The metadata of that workbook. It says which row of a data sheet holds the variable names, which is row 2 for a file written with the labels above them.
ImportCustomDropdown #
import-custom-dropdown
Import custom dropdown choices from the import workbook
Signature:
Public Sub ImportCustomDropdown(ByVal impwb As Workbook, ByVal pasteAtBottom As Boolean)
Walks the dropdown registry of the source workbook, reads the values the import workbook holds for each dropdown, and writes them back. Silently exits when the import workbook has no Choices sheet or the custom dropdown sheet does not exist.
The walk goes through DropdownLists.AllDropdowns, so each name is the one the registry holds and the one Update takes. Reading the ListObject names off the worksheet instead is what used to break this: the generated name ends with the workbook counter, cutting the five characters of "list_" off the front left that counter attached, and the lookup missed for every dropdown ever created. ChoiceKeys builds the key both ends of the round trip use.
Parameters:
impwb: Workbook. The opened import workbook.pasteAtBottom: Boolean. When True, appends choices below existing entries.
Migration Metadata
ImportShowHide #
import-show-hide
Import show/hide choices from a migration workbook
Signature:
Public Sub ImportShowHide(ByVal impwb As Workbook, ByVal meta As ImportMetadata)
Reads the show/hide table of the import workbook and puts each data sheet of the running workbook in the state it describes: visibility, sizes and, on a printed sheet, the direction of each header. Every layer a sheet has is covered - HList and CRF for a data entry sheet, Printed for its companion, VList for an info sheet.
Two faults used to sit here. The visibility read out of the file was written into memory and never reached a sheet, so nothing was ever hidden; and a blank width cell was read as a width of zero and written to the sheet, which is how Excel hides a column, so entries vanished for the opposite reason.
Skips when the file carries no show/hide sheet, and when its show_hide tag
asks for no update.
Parameters:
impwb: Workbook. The opened migration workbook.meta: ImportMetadata. The metadata of that workbook.
ImportEditableLabels #
import-editable-labels
Import editable label values from a migration workbook
Signature:
Public Sub ImportEditableLabels(ByVal impwb As Workbook, ByVal meta As ImportMetadata)
Reads the import workbook dictionary for editable label values and
updates both the worksheet cells and dictionary entries in the source
workbook. Skips when the file carries no Dictionary sheet, when that
dictionary is not prepared, and when the editable_labels tag asks for no
update.
Parameters:
impwb: Workbook. The opened migration workbook.meta: ImportMetadata. The metadata of that workbook.
ImportSingleValues #
import-single-values
Import single metadata values from a migration workbook
Signature:
Public Sub ImportSingleValues(ByVal meta As ImportMetadata)
Takes the epiweek start value the file carries and writes it into the RNG_EpiWeekStart hidden name of the source workbook, then recalculates every sheet that carries an epiweek formula. Skips when the file names no value.
THE NAME HAS NO RANGE BEHIND IT
RNG_EpiWeekStart is a HiddenNames entry and HiddenNames writes a string as a quoted literal in the name definition. So the name exists, Names(...) finds it, and asking that name for a range raises 1004. The write used to go through Names(RNG_EpiWeekStart).RefersToRange.Value under a swallow, which means the value has never once landed. The store is what answers it, the same way FormLogicEpiWeek and CustomLinelistFunctions already read and write it.
Parameters:
meta: ImportMetadata. The metadata of the workbook being imported.
ImportGeobase #
import-geobase
Import geobase data from a workbook
Signature:
Public Sub ImportGeobase(ByVal impwb As Workbook, Optional ByVal histoOnly As Boolean = False)
Delegates to LLGeo.Import to load geobase data. When not histoOnly, also updates geobase headers in HList worksheets via geo.Update and dictionary geo labels via geo.UpdateDict.
THE SHOW/HIDE SHEET IS NOT TOUCHED
A third call used to write the geo labels into per-sheet tables on the show/hide worksheet. Those tables belonged to the first show/hide design and ShowHideStore deletes them on sight now, so the call read a sheet carrying only the store''s own table and raised 91 on every import. The label reaches the form on its own: ShowHide composes it from the dictionary main label that geo.UpdateDict has just rewritten, every time the form is built.
Parameters:
impwb: Workbook. The opened geobase workbook.histoOnly: Optional Boolean. When True, imports only historic geobase data. Defaults to False.
Comparing the two files
CompareWithImportFile #
compare-with-import-file
Compare the labels and the choice lists of the two files
Signature:
Public Sub CompareWithImportFile(ByVal impwb As Workbook)
Walks the dictionary of this linelist and, for every variable the file also carries, files a warning when the two disagree about the label or about the values a choice variable may take.
WHY THE CHOICE LISTS MATTER MORE THAN THE LABELS
A data validation dropdown does not block a programmatic write. CustomTable writes the imported column straight into the cells, so a value the receiving linelist does not offer lands in the cell and stays there: the cell shows something no dropdown can produce, the next hand edit of it is rejected, and every analysis grouping by that variable gains a category the setup never declared. None of that used to appear anywhere.
Run this after ImportData, so the sheets that were touched are known.
Parameters:
impwb: Workbook. The opened import workbook.
Import Report
NeedReport #
need-report
Whether the last import had issues
Signature:
Public Property Get NeedReport() As Boolean
Returns True when at least one report entry exists, meaning some sheets or variables were not imported or not touched during the last import.
Returns: Boolean. True when the import report has entries.
FinalizeReport #
finalize-report
Build the "not touched" part of the import report
Signature:
Public Sub FinalizeReport()
Compares source workbook sheets and variables against the imported items tracked during ImportData. Identifies sheets not touched and variables not touched, skipping formula-type variables (case_when, formula, choice_formula). Call after ImportData to complete the report.
ReportSheets #
report-sheets
Report sheet names for the given scope
Signature:
Public Function ReportSheets(ByVal scope As Byte) As BetterArray
Returns a BetterArray of sheet names that were either not imported (ImportReportNotImported) or not touched (ImportReportNotTouched). Returns an empty BetterArray when no entries exist for the scope.
Parameters:
scope: Byte. ImportReportNotImported or ImportReportNotTouched.
Returns: BetterArray. Sheet names matching the scope, or empty.
ReportVariables #
report-variables
Report variable entries for the given scope
Signature:
Public Function ReportVariables(ByVal scope As Byte) As BetterArray
Returns a BetterArray where each entry is a Variant array of Array(variableName, sheetName). Returns an empty BetterArray when no entries exist for the scope.
Parameters:
scope: Byte. ImportReportNotImported or ImportReportNotTouched.
Returns: BetterArray. Variant arrays of (variableName, sheetName), or empty.
ClearReport #
clear-report
Reset report state
Signature:
Public Sub ClearReport()
Clears all report tracking arrays, drops the diagnostics, and resets the needReport flag. Call before starting a new import session to ensure a clean report.
Internal members (not exported)
Public Accessors
SourceWorkbook #
source-workbook
Source linelist workbook
Signature:
Public Property Get SourceWorkbook() As Workbook
Returns: Workbook. The linelist workbook bound to this importer.
SourceWorkbook #
source-workbook-set
Assign the source linelist workbook
Signature:
Public Property Set SourceWorkbook(ByVal wb As Workbook)
Parameters:
wb: Workbook. The workbook to store.
Seal #
seal
Prevent further changes to setup-only properties
Signature:
Public Sub Seal()
GuardNotSealed #
guard-not-sealed
Raise when a setup-only property is written after sealing
Signature:
Private Sub GuardNotSealed(ByVal propName As String)
Parameters:
propName: String. The name of the property being guarded.
Diagnostics
ImportCheckings #
import-checkings
The checking store, built on first use
Signature:
Private Function ImportCheckings() As Checking
Returns: Checking. The store this import files into.
FileChecking #
file-checking
File one entry, and never raise on a key already used
Signature:
Private Sub FileChecking(ByVal keyName As String, ByVal label As String, _
ByVal scope As Byte)
Checking.Add raises on a key it already holds. An import walks a dictionary, so a key built from a variable name can repeat when the same variable is on two sheets. The counter suffix keeps every entry and keeps the key unique.
Parameters:
keyName: String. The key of the entry.label: String. What to tell the user.scope: Byte. A CheckingScope value.
Migration Metadata
ApplyOneShowHideLayer #
apply-one-show-hide-layer
Put one layer of one sheet in the state the file describes
Signature:
Private Sub ApplyOneShowHideLayer(ByVal impStore As ShowHideStore, _
ByVal dict As LLdictionary, _
ByVal layer As Byte, _
ByVal sheetName As String, _
ByVal targetSh As Worksheet)
Load carries the sizes and the header directions onto the sheet, and Apply is what actually hides and shows. The choices are written into this workbook's own store as well, so they survive the next time it is opened.
Parameters:
impStore: ShowHideStore. The store of the import workbook.dict: LLdictionary. The dictionary of the running workbook.layer: Byte. A ShowHideWorksheetLayer value. Which layer to apply.sheetName: String. The base sheet name.targetSh: Worksheet. The sheet of that layer in the running workbook.
OwnShowHideStore #
own-show-hide-store
The show/hide store of the workbook being written into
Signature:
Private Function OwnShowHideStore() As ShowHideStore
Returns: ShowHideStore. The store, or Nothing when the workbook has no __show_hide worksheet.
CompanionSheet #
companion-sheet
Find a prefixed companion sheet of the running workbook
Signature:
Private Function CompanionSheet(ByVal sheetName As String) As Worksheet
Parameters:
sheetName: String. The full sheet name, prefix included.
Returns: Worksheet. The sheet, or Nothing when the workbook has none.
BaseTableNameOf #
base-table-name-of
The table name a sheet's PRINTSTART anchor is named after
Signature:
Private Function BaseTableNameOf(ByVal sh As Worksheet) As String
A printed companion stores its table name with the print_ prefix in front and the anchor is named after the base one, so the prefix comes off here.
Parameters:
sh: Worksheet. The sheet to read.
Returns: String. The table name with no print_ prefix.
RecalculateEpiWeekSheets #
recalculate-epi-week-sheets
Recompute every sheet whose formulas read the epiweek start
Signature:
Private Sub RecalculateEpiWeekSheets()
Each epiweek formula in the workbook reads RNG_EpiWeekStart, so changing that value leaves every one of them showing the old week until the sheet is recalculated. FormLogicEpiWeek does the same after the user picks a day, and this is the same list of sheet tags.
Comparing the two files
SheetWasImported #
sheet-was-imported
Whether one sheet of this linelist took data from the file
Signature:
Private Function SheetWasImported(ByVal sheetName As String) As Boolean
A comparison run on its own, with no import before it, has no list of touched sheets and answers True for every sheet.
Parameters:
sheetName: String. The sheet to test.
Returns: Boolean. True when the sheet is worth comparing.
CompareOneLabel #
compare-one-label
File a warning when the two files give a variable different labels
Signature:
Private Sub CompareOneLabel(ByVal currVars As LLVariables, _
ByVal inpVars As LLVariables, _
ByVal varName As String)
Parameters:
currVars: LLVariables. The variables of this linelist.inpVars: LLVariables. The variables of the file.varName: String. The variable to compare.
CompareOneChoiceList #
compare-one-choice-list
File a warning when the two files offer a choice variable different values
Signature:
Private Sub CompareOneChoiceList(ByVal currVars As LLVariables, _
ByVal inpVars As LLVariables, _
ByVal currChoi As LLChoices, _
ByVal impChoi As LLChoices, _
ByVal drop As DropdownLists, _
ByVal varName As String)
choice_formula is left alone: its list name is computed from a formula and
the routine that reads that formula lives in the setup, which a linelist does
not carry.
Parameters:
currVars: LLVariables. The variables of this linelist.inpVars: LLVariables. The variables of the file.currChoi: LLChoices. The Choices worksheet of this linelist.impChoi: LLChoices. The Choices worksheet of the file.drop: DropdownLists. The custom dropdowns of this linelist.varName: String. The variable to compare.
ValuesMissingFrom #
values-missing-from
The values of one list that the other does not hold
Signature:
Private Function ValuesMissingFrom(ByVal source As BetterArray, _
ByVal other As BetterArray) As String
Values are matched without regard to case and to surrounding spaces, and they are shown as they were written, so "Yes" against "yes" is reported as a difference a person can act on. The list is capped, because a user reads a handful and the count is what tells them the size.
Parameters:
source: BetterArray. The list to walk.other: BetterArray. The list to look each value up in.
Returns: String. The missing values, joined, empty when there are none.
HoldsValue #
holds-value
Whether a list holds one value, ignoring case and outer spaces
Signature:
Private Function HoldsValue(ByVal listValues As BetterArray, _
ByVal oneValue As String) As Boolean
Parameters:
listValues: BetterArray. The list to search.oneValue: String. The value to look for.
Returns: Boolean. True when the list holds it.
ChoicesOf #
choices-of
The Choices worksheet of one workbook
Signature:
Private Function ChoicesOf(ByVal wb As Workbook) As LLChoices
Parameters:
wb: Workbook. The workbook to read.
Returns: LLChoices. The choices, or Nothing when the workbook carries none.
CustomDropdowns #
custom-dropdowns
The custom dropdown manager of this linelist
Signature:
Private Function CustomDropdowns() As DropdownLists
Returns: DropdownLists. The manager, or Nothing when the workbook has no custom dropdown worksheet.
Import Report
SaveReport #
save-report
Keep the report where it survives the workbook closing
Signature:
Private Sub SaveReport()
The four lists used to live in memory alone and die with this object at the end of the import, while the four ListObjects they belong on sat on __import_rep with nothing writing to them. They are written now, so F_ImportRep can be opened again days later from the advanced form.
A workbook that will not take the store - anything other than a generated linelist - leaves the lists in memory and says so.
FileReportCheckings #
file-report-checkings
Put the four report lists where the user can read them
Signature:
Private Sub FileReportCheckings()
The four lists are the report the form shows. They are filed as checkings as well, so one worksheet carries everything the import found and the user can read it without opening a form.
Private - Source Object Accessors
SourceDictionary #
source-dictionary
Get the dictionary from the source workbook
Signature:
Private Function SourceDictionary() As LLdictionary
Returns: LLdictionary. Dictionary instance from the Dictionary sheet.
PasswordsObject #
passwords-object
Get passwords from the source workbook
Signature:
Private Function PasswordsObject() As Passwords
Returns: Passwords. Passwords instance from the __pass sheet.
GeoObject #
geo-object
Get the geo object from the source workbook
Signature:
Private Function GeoObject() As LLGeo
Returns: LLGeo. Geo instance from the Geo sheet.
Private - Import Helpers
InitializeTracking #
initialize-tracking
Initialize tracking state for a new import session
Signature:
Private Sub InitializeTracking()
The two "not touched" lists are left alone because FinalizeReport replaces them, and the diagnostics are left alone because CheckImportFile runs before this and its entries belong to the same import.
IsReservedSheet #
is-reserved-sheet
Check if a sheet name is reserved (metadata/infrastructure)
Signature:
Private Function IsReservedSheet(ByVal sheetName As String) As Boolean
Parameters:
sheetName: String. The sheet name to check.
Returns: Boolean. True when the name matches a reserved sheet.
ShowHideSheetOf #
show-hide-sheet-of
The show/hide worksheet of a workbook, or Nothing
Signature:
Private Function ShowHideSheetOf(ByVal wb As Workbook) As Worksheet
A file written before the internal-sheet rename carries the old trailing name, so the new name is looked for first and the old one answers when it is absent.
Parameters:
wb: Workbook. The workbook to look through.
Returns: Worksheet. The show/hide worksheet, or Nothing when the workbook has none.
ImportVListSheet #
import-vlist-sheet
Import VList data from a single import sheet to the current sheet
Signature:
Private Sub ImportVListSheet(ByVal currsh As Worksheet, ByVal impsh As Worksheet)
Iterates over the import sheet used range rows, resolving each variable name to a named range on the current sheet. Copies the value when the target cell has no formula. Tracks variables not found as "not imported" report entries.
Parameters:
currsh: Worksheet. The current source VList worksheet.impsh: Worksheet. The import VList worksheet.
ImportHListSheet #
import-hlist-sheet
Import HList data from a single import sheet to the current sheet
Signature:
Private Sub ImportHListSheet(ByVal currsh As Worksheet, ByVal impsh As Worksheet, _
ByVal pasteAtBottom As Boolean, ByVal headerRow As Long)
Creates a CustomTable from the current sheet ListObject and a DataSheet from the import sheet, then delegates to CustomTable.Import. Tracks columns not found in the current table as "not imported" report entries.
Parameters:
currsh: Worksheet. The current source HList worksheet.impsh: Worksheet. The import HList worksheet.pasteAtBottom: Boolean. When True, appends data below existing rows.headerRow: Long. The row of the import sheet that holds the variable names. It was read as row 1 always, so a file written with the labels above the names had its label row read as the header row, and every column of it landed in "not imported".
SheetTagOf #
Get the sheet type tag from worksheet-level HiddenNames.
Signature:
Private Function SheetTagOf(ByVal sh As Worksheet, _
Optional ByVal nameKey As String = "sheet_type") As String
Parameters:
sh: Worksheet. The worksheet to query.nameKey: Optional String. Which worksheet-level name to read.
Returns: String. The sheet type tag (HList, VList, etc.).
Private - Helpers
SheetExists #
sheet-exists
Check if a worksheet exists in a workbook
Signature:
Private Function SheetExists(ByVal wb As Workbook, ByVal sheetName As String) As Boolean
Parameters:
wb: Workbook. The workbook to search.sheetName: String. The sheet name to look for.
Returns: Boolean. True when the sheet exists.
Private - Error Handling
ThrowError #
throw-error
Raise a class-level exception
Signature:
Private Sub ThrowError(ByVal message As String)
Raises a VBA error using vbObjectError + 516, standardising the source to CLASS_NAME.
Parameters:
message: String. Human-readable description of the failure.
Throws:
- vbObjectError Always raises the error.
Used in (13 file(s))
- AnalysisOutput.cls
- ChoiceKeys.cls
- ImportReport.cls
- LLExporter.cls
- Linelist.cls
- LLDataEntry.cls
- CustomLinelistFunctions.bas
- EventsLinelistButtons.bas
- FormLogicAdvanced.bas
- TestImportReport.bas
- TestLLImporter.bas
- TestLLDataEntry.bas
- TestShowHide.bas