From e8ed6cc51da063c36252066b801725bb1cec62fa Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Wed, 27 Mar 2024 12:45:09 -0400 Subject: [PATCH 01/16] Implement analysis migration for VDI --- Model/pom.xml | 6 + .../fix/table/edaanalysis/AnalysisRow.java | 25 ++++ .../plugins/VDIMigrationPlugin.java | 124 ++++++++++++++++++ .../plugins/VDIMigrationPluginTest.java | 41 ++++++ .../test/resources/migration-unit-test-1.json | 11 ++ 5 files changed, 207 insertions(+) create mode 100644 Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java create mode 100644 Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java create mode 100644 Model/src/test/resources/migration-unit-test-1.json diff --git a/Model/pom.xml b/Model/pom.xml index 14c53f624..5bbab219b 100644 --- a/Model/pom.xml +++ b/Model/pom.xml @@ -161,6 +161,12 @@ test + + org.mockito + mockito-core + test + + org.irods.jargon jargon-core diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/AnalysisRow.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/AnalysisRow.java index 0201971b2..8d6c2879e 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/AnalysisRow.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/AnalysisRow.java @@ -33,6 +33,15 @@ public AnalysisRow(ResultSet rs, DBPlatform platform) throws SQLException { _numVisualizations = rs.getInt("num_visualizations"); } + public AnalysisRow(String analysisId, String datasetId, JSONObject descriptor, int numFilters, int numComputations, int numVisualizations) { + _analysisId = analysisId; + _datasetId = datasetId; + _descriptor = descriptor; + _numFilters = numFilters; + _numComputations = numComputations; + _numVisualizations = numVisualizations; + } + public Object[] toOrderedValues() { return new Object[] { _datasetId, _descriptor.toString(), _numFilters, _numComputations, _numVisualizations, _analysisId @@ -61,6 +70,22 @@ public JSONObject getDescriptor() { return _descriptor; } + public String getAnalysisId() { + return _analysisId; + } + + public int getNumFilters() { + return _numFilters; + } + + public int getNumComputations() { + return _numComputations; + } + + public int getNumVisualizations() { + return _numVisualizations; + } + /** * Sets a new descriptor and refreshes the stats (number of filters, computations, * and visualizations) on this analysis. diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java new file mode 100644 index 000000000..109e18b85 --- /dev/null +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -0,0 +1,124 @@ +package org.gusdb.wdk.model.fix.table.edaanalysis.plugins; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.log4j.Logger; +import org.gusdb.fgputil.json.JsonUtil; +import org.gusdb.wdk.model.WdkModel; +import org.gusdb.wdk.model.fix.table.TableRowInterfaces; +import org.gusdb.wdk.model.fix.table.edaanalysis.AbstractAnalysisUpdater; +import org.gusdb.wdk.model.fix.table.edaanalysis.AnalysisRow; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class VDIMigrationPlugin extends AbstractAnalysisUpdater { + private static final Logger LOG = Logger.getLogger(VDIMigrationPlugin.class); + public static final String UD_DATASET_ID_PREFIX = "EDAUD_"; + + private Map legacyIdToVdiId; + private int missingFromVdiCount = 0; + + @Override + public TableRowInterfaces.RowResult processRecord(AnalysisRow nextRow) throws Exception { + final String legacyDatasetId = nextRow.getDatasetId(); + final String legacyUdId = legacyDatasetId.replace(UD_DATASET_ID_PREFIX, ""); + final String vdiId = legacyIdToVdiId.get(legacyUdId); + + if (vdiId == null) { + LOG.warn("Unable to find legacy ID " + legacyUdId + " in the tinydb file."); + missingFromVdiCount++; + return new TableRowInterfaces.RowResult<>(nextRow); + } + + // Append UD prefix to VDI ID. The prefix is prepended in the view that maps stable VDI IDs to the unstable study + // ID, which is the currency of EDA. + final String vdiDatasetId = UD_DATASET_ID_PREFIX + vdiId; + + // Create a copy with just the dataset ID updated to VDI counterpart. + AnalysisRow out = new AnalysisRow(nextRow.getAnalysisId(), vdiDatasetId, nextRow.getDescriptor(), + nextRow.getNumFilters(), nextRow.getNumComputations(), nextRow.getNumVisualizations()); + + return new TableRowInterfaces.RowResult<>(out); + } + + @Override + public void dumpStatistics() { + if (missingFromVdiCount > 0) { + LOG.warn("Failed to migrate " + missingFromVdiCount + " datasets, they were not found in the provided tinydb file."); + } + } + + @Override + public void configure(WdkModel wdkModel, List additionalArgs) throws Exception { + // Parse args in the format --= + final Map args = additionalArgs.stream() + .map(arg -> Arrays.stream(arg.split("=")) + .map(String::trim) // Trim whitespace from args + .collect(Collectors.toList())) + .collect(Collectors.toMap( + pair -> pair.get(0), + pair -> pair.size() > 1 ? pair.get(1) : "true")); // A flag without an "=" is a boolean. Set true if present. + + // Validate required arg. + if (!args.containsKey("--tinyDb")) { + throw new IllegalArgumentException("Missing required flag --tinyDb"); + } + final File tinyDbFile = new File(args.get("--tinyDb")); + + this.legacyIdToVdiId = readLegacyStudyIdToVdiId(tinyDbFile); + + // Default to dryrun to avoid incidental migrations when testing. + this._writeToDb = Boolean.parseBoolean(args.getOrDefault("--liveRun", "false")); + } + + /** + * Parse the tinydb file into a map of legacy UD identifiers to VDI identifiers. + * + * Example file format: + * + * { + * "_default": { + * "1": { + * "type": "owner", + * "udId": 1234, + * "vdiId": "123XyZ", + * "msg": null, + * "time": "Fri Mar 26 00:00:00 2024" + * } + * } + * + * @param tinyDbFile TinyDB file, output of the migration script run to migrate legacy UDs into VDI. + * @return Map of legacy UD Ids to VDI Ids. + */ + private Map readLegacyStudyIdToVdiId(File tinyDbFile) { + try { + JsonNode root = JsonUtil.Jackson.readTree(tinyDbFile); + JsonNode dbRoot = root.get("_default"); + + Map mapping = new HashMap<>(); + Iterator> fieldIterator = dbRoot.fields(); + + // Iterate through each field in the "_default" node. + // Ignore the numeric index keys and extract the udId and vdiId fields to create mapping. + while (fieldIterator.hasNext()) { + Map.Entry entry = fieldIterator.next(); + mapping.put(entry.getValue().get("udId").asText(), entry.getValue().get("vdiId").asText()); + } + + LOG.info("Extracted a mapping of " + mapping.size() + " legacy to VDI identifiers."); + return mapping; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private enum CliArg { + + } +} diff --git a/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java b/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java new file mode 100644 index 000000000..8f637705e --- /dev/null +++ b/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java @@ -0,0 +1,41 @@ +package org.gusdb.wdk.model.fix.table.edaanalysis.plugins; + +import org.gusdb.wdk.model.WdkModel; +import org.gusdb.wdk.model.fix.table.TableRowInterfaces; +import org.gusdb.wdk.model.fix.table.edaanalysis.AnalysisRow; +import org.json.JSONObject; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.io.File; +import java.util.List; +import java.util.Objects; + +public class VDIMigrationPluginTest { + private WdkModel mockedModel; + private ClassLoader classLoader; + + @Before + public void setup() { + classLoader = getClass().getClassLoader(); + mockedModel = Mockito.mock(WdkModel.class); + } + + @Test + public void test() throws Exception { + final File file = new File(Objects.requireNonNull(classLoader.getResource("migration-unit-test-1.json")).getFile()); + final VDIMigrationPlugin migrationPlugin = new VDIMigrationPlugin(); + final List args = List.of("--tinyDb=" + file.getPath()); + migrationPlugin.configure(mockedModel, args); + TableRowInterfaces.RowResult result = migrationPlugin.processRecord( + new AnalysisRow("x", + "EDAUD_1234", + new JSONObject(), + 3, + 4, + 5)); + Assert.assertEquals("EDAUD_123XyZ", result.getRow().getDatasetId()); + } +} diff --git a/Model/src/test/resources/migration-unit-test-1.json b/Model/src/test/resources/migration-unit-test-1.json new file mode 100644 index 000000000..b6f52f60e --- /dev/null +++ b/Model/src/test/resources/migration-unit-test-1.json @@ -0,0 +1,11 @@ +{ + "_default": { + "1": { + "type": "owner", + "udId": 1234, + "vdiId": "123XyZ", + "msg": null, + "time": "Fri Mar 26 00:00:00 2024" + } + } +} From 25c706d1e0431c3dde9d7bfbe8dfa5897b1758cc Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Tue, 2 Apr 2024 11:22:03 -0400 Subject: [PATCH 02/16] Updates to support IDs --- Model/pom.xml | 5 + .../plugins/VDIEntityIdRetriever.java | 24 +++++ .../plugins/VDIMigrationPlugin.java | 100 ++++++++++++------ .../plugins/VDIMigrationPluginTest.java | 15 ++- .../test/resources/analysis-unit-test-1.json | 61 +++++++++++ 5 files changed, 172 insertions(+), 33 deletions(-) create mode 100644 Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java create mode 100644 Model/src/test/resources/analysis-unit-test-1.json diff --git a/Model/pom.xml b/Model/pom.xml index 5bbab219b..847794e4b 100644 --- a/Model/pom.xml +++ b/Model/pom.xml @@ -110,6 +110,11 @@ commons-lang3 + + commons-codec + commons-codec + + com.fasterxml.jackson.core jackson-databind diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java new file mode 100644 index 000000000..506c01832 --- /dev/null +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java @@ -0,0 +1,24 @@ +package org.gusdb.wdk.model.fix.table.edaanalysis.plugins; + +import org.gusdb.fgputil.db.runner.SQLRunner; + +import javax.sql.DataSource; + +public class VDIEntityIdRetriever { + private DataSource eda; + + public VDIEntityIdRetriever(DataSource eda) { + this.eda = eda; + } + + public String queryEntityId(String vdiStableId) { + final String sql = "SELECT internal_abbrev FROM userstudydatasetid u" + + "JOIN vdi_datasets_dev_s.entitytypegraph etg" + + "ON u.study_stable_id = etg.study_stable_id" + + "WHERE dataset_stable_id = ?"; + return new SQLRunner(eda, sql).executeQuery(new Object[] { vdiStableId }, rs -> { + rs.next(); + return rs.getString("internal_abbrev"); + }); + } +} diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java index 109e18b85..a63dbde9d 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -1,34 +1,73 @@ package org.gusdb.wdk.model.fix.table.edaanalysis.plugins; import com.fasterxml.jackson.databind.JsonNode; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.log4j.Logger; import org.gusdb.fgputil.json.JsonUtil; import org.gusdb.wdk.model.WdkModel; import org.gusdb.wdk.model.fix.table.TableRowInterfaces; import org.gusdb.wdk.model.fix.table.edaanalysis.AbstractAnalysisUpdater; import org.gusdb.wdk.model.fix.table.edaanalysis.AnalysisRow; +import org.json.JSONObject; import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Collectors; public class VDIMigrationPlugin extends AbstractAnalysisUpdater { private static final Logger LOG = Logger.getLogger(VDIMigrationPlugin.class); - public static final String UD_DATASET_ID_PREFIX = "EDAUD_"; + private static final String UD_DATASET_ID_PREFIX = "EDAUD_"; + private static final Pattern VAR_ID_PATTERN = Pattern.compile("variableId\":\\s*\"([a-zA-Z0-9_-]+)"); + private static final Pattern ENTITY_ID_PATTERN = Pattern.compile("entityId\":\\s*\"([a-zA-Z0-9_-]+)"); - private Map legacyIdToVdiId; + private Map _legacyIdToVdiId; + private VDIEntityIdRetriever _vdiEntityIdRetriever; private int missingFromVdiCount = 0; + @Override + public void configure(WdkModel wdkModel, List additionalArgs) throws Exception { + configure(wdkModel, additionalArgs, new VDIEntityIdRetriever(wdkModel.getAppDb().getDataSource())); + } + + // Visible for testing. + void configure(WdkModel wdkModel, List additionalArgs, VDIEntityIdRetriever entityIdRetriever) { + // Parse args in the format --= + final Map args = additionalArgs.stream() + .map(arg -> Arrays.stream(arg.split("=")) + .map(String::trim) // Trim whitespace from args + .collect(Collectors.toList())) + .collect(Collectors.toMap( + pair -> pair.get(0), + pair -> pair.size() > 1 ? pair.get(1) : "true")); // A flag without an "=" is a boolean. Set true if present. + + // Validate required arg. + if (!args.containsKey("--tinyDb")) { + throw new IllegalArgumentException("Missing required flag --tinyDb"); + } + final File tinyDbFile = new File(args.get("--tinyDb")); + + _legacyIdToVdiId = readLegacyStudyIdToVdiId(tinyDbFile); + + // Default to dryrun to avoid incidental migrations when testing. + _writeToDb = Boolean.parseBoolean(args.getOrDefault("--liveRun", "false")); + _wdkModel = wdkModel; + _vdiEntityIdRetriever = entityIdRetriever; + } + @Override public TableRowInterfaces.RowResult processRecord(AnalysisRow nextRow) throws Exception { final String legacyDatasetId = nextRow.getDatasetId(); final String legacyUdId = legacyDatasetId.replace(UD_DATASET_ID_PREFIX, ""); - final String vdiId = legacyIdToVdiId.get(legacyUdId); + final String vdiId = _legacyIdToVdiId.get(legacyUdId); if (vdiId == null) { LOG.warn("Unable to find legacy ID " + legacyUdId + " in the tinydb file."); @@ -39,14 +78,40 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR // Append UD prefix to VDI ID. The prefix is prepended in the view that maps stable VDI IDs to the unstable study // ID, which is the currency of EDA. final String vdiDatasetId = UD_DATASET_ID_PREFIX + vdiId; + final String vdiEntityId = _vdiEntityIdRetriever.queryEntityId(vdiDatasetId); + + String descriptor = nextRow.getDescriptor().toString(); + + // Find all variable IDs. + final Set legacyVariableIds = VAR_ID_PATTERN.matcher(descriptor).results() + .map(match -> match.group(1)) + .collect(Collectors.toSet()); + + final String entityId = ENTITY_ID_PATTERN.matcher(descriptor).results() + .findAny() + .map(m -> m.group(1)) + .orElse(null); + + if (entityId != null) { + descriptor = descriptor.replaceAll(entityId, vdiEntityId); + } + + for (String legacyVariableId: legacyVariableIds) { + descriptor = descriptor.replaceAll(legacyVariableId, convertToVdiId(legacyVariableId)); + } // Create a copy with just the dataset ID updated to VDI counterpart. - AnalysisRow out = new AnalysisRow(nextRow.getAnalysisId(), vdiDatasetId, nextRow.getDescriptor(), + AnalysisRow out = new AnalysisRow(nextRow.getAnalysisId(), vdiDatasetId, new JSONObject(descriptor), nextRow.getNumFilters(), nextRow.getNumComputations(), nextRow.getNumVisualizations()); return new TableRowInterfaces.RowResult<>(out); } + private String convertToVdiId(String legacyVariableId) { + byte[] encodedId = DigestUtils.digest(DigestUtils.getSha1Digest(), legacyVariableId.getBytes(StandardCharsets.UTF_8)); + return "VAR_" + Hex.encodeHexString(encodedId).substring(0, 16); + } + @Override public void dumpStatistics() { if (missingFromVdiCount > 0) { @@ -54,29 +119,6 @@ public void dumpStatistics() { } } - @Override - public void configure(WdkModel wdkModel, List additionalArgs) throws Exception { - // Parse args in the format --= - final Map args = additionalArgs.stream() - .map(arg -> Arrays.stream(arg.split("=")) - .map(String::trim) // Trim whitespace from args - .collect(Collectors.toList())) - .collect(Collectors.toMap( - pair -> pair.get(0), - pair -> pair.size() > 1 ? pair.get(1) : "true")); // A flag without an "=" is a boolean. Set true if present. - - // Validate required arg. - if (!args.containsKey("--tinyDb")) { - throw new IllegalArgumentException("Missing required flag --tinyDb"); - } - final File tinyDbFile = new File(args.get("--tinyDb")); - - this.legacyIdToVdiId = readLegacyStudyIdToVdiId(tinyDbFile); - - // Default to dryrun to avoid incidental migrations when testing. - this._writeToDb = Boolean.parseBoolean(args.getOrDefault("--liveRun", "false")); - } - /** * Parse the tinydb file into a map of legacy UD identifiers to VDI identifiers. * @@ -117,8 +159,4 @@ private Map readLegacyStudyIdToVdiId(File tinyDbFile) { throw new RuntimeException(e); } } - - private enum CliArg { - - } } diff --git a/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java b/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java index 8f637705e..4842ab345 100644 --- a/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java +++ b/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java @@ -3,39 +3,50 @@ import org.gusdb.wdk.model.WdkModel; import org.gusdb.wdk.model.fix.table.TableRowInterfaces; import org.gusdb.wdk.model.fix.table.edaanalysis.AnalysisRow; +import org.hamcrest.MatcherAssert; import org.json.JSONObject; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Matchers; import org.mockito.Mockito; import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Objects; public class VDIMigrationPluginTest { private WdkModel mockedModel; private ClassLoader classLoader; + private VDIEntityIdRetriever retriever; @Before public void setup() { classLoader = getClass().getClassLoader(); mockedModel = Mockito.mock(WdkModel.class); + retriever = Mockito.mock(VDIEntityIdRetriever.class); } @Test public void test() throws Exception { + File analysisFile = new File(Objects.requireNonNull(classLoader.getResource("analysis-unit-test-1.json")).getFile()); + JSONObject descriptor = new JSONObject(Files.readString(Path.of(analysisFile.getPath()))); final File file = new File(Objects.requireNonNull(classLoader.getResource("migration-unit-test-1.json")).getFile()); final VDIMigrationPlugin migrationPlugin = new VDIMigrationPlugin(); final List args = List.of("--tinyDb=" + file.getPath()); - migrationPlugin.configure(mockedModel, args); + Mockito.when(retriever.queryEntityId("EDAUD_123XyZ")).thenReturn("EDAUD_Migrated_ID"); + migrationPlugin.configure(mockedModel, args, retriever); TableRowInterfaces.RowResult result = migrationPlugin.processRecord( new AnalysisRow("x", "EDAUD_1234", - new JSONObject(), + descriptor, 3, 4, 5)); Assert.assertEquals("EDAUD_123XyZ", result.getRow().getDatasetId()); + Assert.assertTrue(result.getRow().getDescriptor().toString().contains("VAR_c73e53adb951e2fe")); } } diff --git a/Model/src/test/resources/analysis-unit-test-1.json b/Model/src/test/resources/analysis-unit-test-1.json new file mode 100644 index 000000000..a973aeeb2 --- /dev/null +++ b/Model/src/test/resources/analysis-unit-test-1.json @@ -0,0 +1,61 @@ +{ + "descriptor": { + "subset": { + "descriptor": [], + "uiSettings": {} + }, + "computations": [ + { + "computationId": "pass-through", + "displayName": "", + "descriptor": { + "type": "pass" + }, + "visualizations": [ + { + "visualizationId": "b28b42de-4a74-4a47-8232-b5c0ce7eb36d", + "displayName": "File vs. Database Subsetting Duration", + "descriptor": { + "type": "scatterplot", + "configuration": { + "valueSpecConfig": "Best fit line with raw", + "independentAxisLogScale": false, + "dependentAxisLogScale": false, + "xAxisVariable": { + "entityId": "TEMP_diy_performance_numericoutput", + "variableId": "TEMP_DB_DURATION" + }, + "yAxisVariable": { + "entityId": "TEMP_diy_performance_numericoutput", + "variableId": "TEMP_MAP_REDUCE_DURATION" + }, + "overlayVariable": { + "entityId": "TEMP_diy_performance_numericoutput", + "variableId": "TEMP_NUM_FILTER_VARS" + } + }, + "currentPlotFilters": [], + "thumbnail": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAHCAu4DAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA4r43+NdV+GvwX8ffEbQre0n1Lwr4X1XW7OK7RmgkntrSSaNZFVlYoWQAgMpxnBHWgDn/8AhHP2p/8Aosnwq/8ADaaj/wDL2gA/4Rz9qf8A6LJ8Kv8A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PVaACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAPKv2sf+TWPjJ/2T/xD/wCm6egD1WgAoAKACgAoAKACgAoAKACgCFv+PtP+ubfzWgCagAoAKACgAoAKACgAoAKACgCGz/49If8Armv8qAJqACgAoAKACgAoAKACgAoAhk/4+Yfo39KAJqACgAoAKACgAoAKACgAoAKAIbT/AFA+rfzNAE1ABQAUAFABQAUAFABQAUAQzf623/66H/0BqAJqACgAoAKACgAoAKACgAoAKAIbb7j/APXR/wD0I0ATUAFABQAUAFABQAUAFABQB5V+0t/yTrSP+ygeBP8A1KtLoA9VoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA8q/ax/5NY+Mn/ZP/ABD/AOm6egD1WgAoAKACgAoAKACgAoAKACgCFv8Aj7T/AK5t/NaAJqACgAoAKACgAoAKACgAoAKAIbP/AI9If+ua/wAqAJqACgAoAKACgAoAKACgAoAhk/4+Yfo39KAJqACgAoAKACgAoAKACgAoAKAIbT/UD6t/M0ATUAFABQAUAFABQAUAFABQBDN/rbf/AK6H/wBAagCagAoAKACgAoAKACgAoAKACgCG2+4//XR//QjQBNQAUAFABQAUAFABQAUAFAHlX7S3/JOtI/7KB4E/9SrS6APVaACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAPKv2sf8Ak1j4yf8AZP8AxD/6bp6APVaACgAoAKACgAoAKACgAoAKAIW/4+0/65t/NaAJqACgAoAKACgAoAKACgAoAKAIbP8A49If+ua/yoAmoAKACgAoAKACgAoAKACgCGT/AI+Yfo39KAJqACgAoAKACgAoAKACgAoAKAIbT/UD6t/M0ATUAFABQAUAFABQAUAFABQBDN/rbf8A66H/ANAagCagAoAKACgAoAKACgAoAKACgCG2+4//AF0f/wBCNAE1ABQAUAFABQAUAFABQAUAeVftLf8AJOtI/wCygeBP/Uq0ugD1WgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgDyr9rH/k1j4yf9k/8Q/+m6egD1WgAoAKACgAoAKACgAoAKACgCFv+PtP+ubfzWgCagAoAKACgAoAKACgAoAKACgCGz/49If+ua/yoAmoAKAC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+hQAedF/z1T/AL6FAB50X/PVP++hQAedF/z1T/voUAHnRf8APVP++hQAedF/z1T/AL6FAB50X/PVP++hQAedF/z1T/voUARWksQtYQZFBEa9/agCXzov+eqf99CgA86L/nqn/fQoAPOi/wCeqf8AfQoAPOi/56p/30KADzov+eqf99CgA86L/nqn/fQoAPOi/wCeqf8AfQoAPOi/56p/30KADzov+eqf99CgCKSWL7REfMXADd/pQBL50X/PVP8AvoUAHnRf89U/76FAB50X/PVP++hQAedF/wA9U/76FAB50X/PVP8AvoUAHnRf89U/76FAB50X/PVP++hQAedF/wA9U/76FAB50X/PVP8AvoUAHnRf89U/76FAEVrLEIQDIo5bv7mgCXzov+eqf99CgA86L/nqn/fQoAPOi/56p/30KADzov8Anqn/AH0KADzov+eqf99CgA86L/nqn/fQoAPOi/56p/30KADzov8Anqn/AH0KADzov+eqf99CgCKaWIywESLxIc8/7LUAS+dF/wA9U/76FAB50X/PVP8AvoUAHnRf89U/76FAB50X/PVP++hQAedF/wA9U/76FAB50X/PVP8AvoUAHnRf89U/76FAB50X/PVP++hQAedF/wA9U/76FAB50X/PVP8AvoUARW0sQRsyL/rH7/7RoAl86L/nqn/fQoAPOi/56p/30KADzov+eqf99CgA86L/AJ6p/wB9CgA86L/nqn/fQoAPOi/56p/30KADzov+eqf99CgA86L/AJ6p/wB9CgA86L/nqn/fQoA8r/aVkjb4d6OFkUn/AIWB4E4B/wCpq0ugD1egAoAKACgAoAKACgAoAKACgAoAKACgAoA57WviH4D8OR6xLrvjHRrBfD8UM+q+fexobJJjiEygnKbzwmfvHgZNAGloOv6J4o0e08QeHNVtdT02+jEttd2sokilT1VhweQR7EEUAX6ACgAoA8q/ax/5NY+Mn/ZP/EP/AKbp6APUvJh/55J/3yKADyYf+eSf98igA8mH/nkn/fIoAPJh/wCeSf8AfIoAPJh/55J/3yKADyYf+eSf98igA8mH/nkn/fIoAPJh/wCeSf8AfIoAPJh/55J/3yKADyYf+eSf98igCEwxfa0HlJjy2/hHqKAJvJh/55J/3yKADyYf+eSf98igA8mH/nkn/fIoAPJh/wCeSf8AfIoA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AUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAeSeNvj3YfCn4gXWgfFC1t9H8M3ekSanoeurIzLcy26brqzkXHyzgYeMDIdTgfN8tAHX/C7xJ4q8Y+CrDxT4v8ADK+HrzVN11DppkLy29sxzCJiQMSlNpZcDaTjqDQBh+KP2ifg94L8UXHg/wAU+LX07UbOWCC5MumXZtbd5lRohJdCIwJlZEOWcAZ5xQBqah8ZvhlpI8V/2r4ttrI+B1jfXkuY5Ims1kUNE21lBkDgjYUDBiQFyeKAMmf41eHdI1vxNN4m13SNN8OeH9F07WHnmFzFeQpdGQKZopIgF3FAqIpaTd8rKCQCAbfw++LXgH4ojUB4J1qa8k0polvIp7C5s5YfMDGMmO4jRsMFbBAwcGgDr6ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAPBPjb8HPGH7Qnip/CevPceHfBPhu0N9pd9DNE82pa5JGyxThVYukNsGPyt5Zd2P3lAIANjSPhjqvxW8L6Jqnxx03XtE8WaXA1heR6N4mntrW6KOf8ASUFpMq7ZOHAcB1yVIwoJAPGvj78Lfjz47vPiJoUng3xX4qXWJkHhee08XQ6folnZBIyFlszMnmTq4cEyKwc4I2gZIB0vxp+DnxR+InxTi+Kmh+CNGT/hX62iaTpt9NEZPF+2ZZpVmkWQrBHGR+4EoJ8zLMFB4ANb4gfDTxT4p134ka5qnwpvtd0rxX4e8N2sGlJrdtZXTTwXEskwWUSFUlt96SAk7HZAoZgSaAOn/Z10T4uaK3iaL4gXHiM+H3ltP+Ebg8T6haX2sRqI2+0mea2ypUuU2BmZgFbOM8gHs1ABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAf/Z" + } + }, + { + "visualizationId": "15bb242d-b7d6-439c-86ee-98b5334775ed", + "displayName": "Unnamed visualization", + "descriptor": { + "type": "histogram", + "configuration": { + "dependentAxisLogScale": false, + "valueSpec": "count" + }, + "currentPlotFilters": [], + "thumbnail": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAHCAu4DAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAralLJb6ddTxNteOF3U4zghSRQBZoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgCnrH/IJvf8Ar2k/9BNAFygAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAKesf8AIJvf+vaT/wBBNAFygAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAKesf8gm9/wCvaT/0E0AXKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAp6x/wAgm9/69pP/AEE0AXKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAp6x/yCb3/AK9pP/QTQBcoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgCnrH/ACCb3/r2k/8AQTQBcoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgCnrH/IJvf8Ar2k/9BNAFygAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAKesf8AIJvf+vaT/wBBNAE32f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAKkOxg3nSNjsW4oAloAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAp6x/yCb3/r2k/wDQTQBcoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgCnrH/IJvf+vaT/0E0AXKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAp6x/yCb3/r2k/wDQTQBcoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgCnrH/IJvf+vaT/0E0AXKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAp6x/yCb3/r2k/wDQTQBcoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgCnrH/IJvf+vaT/0E0AXKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAp6x/yCb3/r2k/wDQTQBcoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgCnrH/IJvf+vaT/0E0ATfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAfZ/+m83/AH1QAfZ/+m83/fVAB9n/AOm83/fVAB9n/wCm83/fVAB9n/6bzf8AfVAB9n/6bzf99UAH2f8A6bzf99UAH2f/AKbzf99UAH2f/pvN/wB9UAH2f/pvN/31QAfZ/wDpvN/31QAfZ/8ApvN/31QAqQ7GDedI2OxbigCWgAoAKACgAoAKACgAoAKACgAoAKACgAoA5/RfG+ia/wCKfEng+w+0fb/Cr2qX/mR4jzcQiaPY2fm+U88DBoAPA3jjRPiFob+IfD5mNol9eafmVQC0ltcPBIVKkhlLRsVYHkEdOgAOgoAKACgCnrH/ACCb3/r2k/8AQTQBcoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA+MdV+BHw71H4reMPBPwo/Zs8H6k3hsac2p3fiDXrm2gElxb+YiW0EaOQu3lmJALZGOKAPqT4X6LqPh3wNpmiar4V8PeG7i0EiHTNActYwL5jFfLJRD8wIZvlHzM3XqQDqqACgAoAp6x/yCb3/r2k/9BNAFygAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD5o+MHgK1v/AIn6r4ht/gr8UNZvLuC1hl1bw/4rSwtrhI4xtRY/tUZAQlgQVA3biPvZIB6d+ztrWheIPhFo2qeHNP16wsXlvIlttdvmu76J47qVJFlkZ2YkOrcE5UYGBjFAHpNABQAUAU9Y/wCQTe/9e0n/AKCaALlABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAHzH8b/HWsad8SdQ0Hwn8Qfi9fXVtbW82oaJ4I8MWWpRaSjx/uzLJLHuRpQrOF3M3UgAYFAHq37PcHgOL4TaPL8ONX1LVdGunuro3uqHN7PcyXEjXDXHyr+9ExkDfKMEYHGKAPRqACgAoAp6x/yCb3/r2k/wDQTQBcoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgCOeeG2hkubiVY4olLu7nCqoGSSewAoA+edL8L+NPiDdXvx9+AXxEufCdr4/tIJL7Ttc8PR3TXJtkaCC7hBlXyi0SrtUna42s2OAADtf2YYPDkfwZ0e48NatqmqR3lxfXN9earbC2vJr97uU3RlhUkRsJvMG0E4AHLdSAeq0AFABQBT1j/kE3v/AF7Sf+gmgC5QAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAFXVNOttX0270m9Utb3sElvKAcEo6lW/QmgD598Kah+1J8KPCun/AAt0/wCCmj+MofD9qml6X4jg8SwWNvLaxqEgae2lXzVdUCBwpIYg4PcgHqPwS8A6t8N/h3ZeHfEWo21/rc1xd6nq1zaoVhkvLq4knl8sHB2K0hVSQCQoOBnFAHd0AFABQBT1j/kE3v8A17Sf+gmgC5QAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAFPWP8AkE3v/XtJ/wCgmgC5QAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAFPWP+QTe/wDXtJ/6CaAJvs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAD7P/03m/76oAPs/wD03m/76oAPs/8A03m/76oAPs//AE3m/wC+qAFSHYwbzpGx2LcUAS0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAU9Y/5BN7/17Sf+gmgC5QAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAFPWP+QTe/8AXtJ/6CaALlABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAU9Y/5BN7/17Sf+gmgC5QAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAFPWP+QTe/8AXtJ/6CaALlABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAU9Y/5BN7/17Sf+gmgC5QAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAFPWP+QTe/8AXtJ/6CaALlABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAU9Y/5BN7/17Sf+gmgC5QAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAFPWP+QTe/8AXtJ/6CaAJvs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgA+z/APTeb/vqgA+z/wDTeb/vqgA+z/8ATeb/AL6oAPs//Teb/vqgBUh2MG86Rsdi3FAEtABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAFPWP+QTe/wDXtJ/6CaALlABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAU9Y/wCQTe/9e0n/AKCaALlABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAU9Y/5BN7/ANe0n/oJoAuUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBT1j/AJBN7/17Sf8AoJoAuUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBT1j/kE3v8A17Sf+gmgC5QAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAFPWP8AkE3v/XtJ/wCgmgC5QAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAFPWP+QTe/wDXtJ/6CaALlABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUARXMCXVvLbSEhZkaNiOuCMcUAQfYrn/oMXf/AHzD/wDEUAH2K5/6DF3/AN8w/wDxFAB9iuf+gxd/98w//EUAH2K5/wCgxd/98w//ABFAB9iuf+gxd/8AfMP/AMRQAfYrn/oMXf8A3zD/APEUAH2K5/6DF3/3zD/8RQAfYrn/AKDF3/3zD/8AEUAH2K5/6DF3/wB8w/8AxFAB9iuf+gxd/wDfMP8A8RQAfYrn/oMXf/fMP/xFAB9iuf8AoMXf/fMP/wARQAfYrn/oMXf/AHzD/wDEUAH2K5/6DF3/AN8w/wDxFAB9iuf+gxd/98w//EUAH2K5/wCgxd/98w//ABFAB9iuf+gxd/8AfMP/AMRQAfYrn/oMXf8A3zD/APEUAH2K5/6DF3/3zD/8RQAfYrn/AKDF3/3zD/8AEUAH2K5/6DF3/wB8w/8AxFAB9iuf+gxd/wDfMP8A8RQAfYrn/oMXf/fMP/xFAB9iuf8AoMXf/fMP/wARQAfYrn/oMXf/AHzD/wDEUAH2K5/6DF3/AN8w/wDxFAB9iuf+gxd/98w//EUAH2K5/wCgxd/98w//ABFAB9iuf+gxd/8AfMP/AMRQAfYrn/oMXf8A3zD/APEUAH2K5/6DF3/3zD/8RQAfYrn/AKDF3/3zD/8AEUAH2K5/6DF3/wB8w/8AxFAB9iuf+gxd/wDfMP8A8RQAfYrn/oMXf/fMP/xFAB9iuf8AoMXf/fMP/wARQAosrkf8xe7P/AYv/iKAE+xXP/QYu/8AvmH/AOIoAPsVz/0GLv8A75h/+IoAPsVz/wBBi7/75h/+IoAPsVz/ANBi7/75h/8AiKAD7Fc/9Bi7/wC+Yf8A4igA+xXP/QYu/wDvmH/4igA+xXP/AEGLv/vmH/4igA+xXP8A0GLv/vmH/wCIoAPsVz/0GLv/AL5h/wDiKAD7Fc/9Bi7/AO+Yf/iKAD7Fc/8AQYu/++Yf/iKAD7Fc/wDQYu/++Yf/AIigA+xXP/QYu/8AvmH/AOIoAPsVz/0GLv8A75h/+IoAPsVz/wBBi7/75h/+IoAPsVz/ANBi7/75h/8AiKAD7Fc/9Bi7/wC+Yf8A4igA+xXP/QYu/wDvmH/4igA+xXP/AEGLv/vmH/4igA+xXP8A0GLv/vmH/wCIoAPsVz/0GLv/AL5h/wDiKAD7Fc/9Bi7/AO+Yf/iKAD7Fc/8AQYu/++Yf/iKAD7Fc/wDQYu/++Yf/AIigA+xXP/QYu/8AvmH/AOIoAPsVz/0GLv8A75h/+IoAPsVz/wBBi7/75h/+IoAPsVz/ANBi7/75h/8AiKAD7Fc/9Bi7/wC+Yf8A4igA+xXP/QYu/wDvmH/4igA+xXP/AEGLv/vmH/4igA+xXP8A0GLv/vmH/wCIoAPsVz/0GLv/AL5h/wDiKAD7Fc/9Bi7/AO+Yf/iKAD7Fc/8AQYu/++Yf/iKAD7Fc/wDQYu/++Yf/AIigB8VrPHIHfUrmUDqjrGAfyQH9aALNABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAFXU7w6fpt3fqgc20EkwUnG7apOP0oA+atB/bQi1L4Bal8TL/wnHb+LrG7t9Oi8Oi4P+kz3SLLaOjEbvKeBjKSQOIpcdKAPQ4P2lvh5o3gfwd4m+IGozaRfeKvDtrr/ANltNMvL5YY5IUdyWgifail8ZbHSgDbufixo8niPwlFpGt6Hc6B4m0jUNYS5Mk5nlgt0icSQBIzGVAl+cOytyNoJBAAKvgj9pH4J/EbX7Twx4N8dQ6hqWoRPNaRfY7mFbhUTe4jeSNUd1XlkBLLg5AwcAHpdABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBS1u3mvNFv7S3TfLPayxxrkDLFCAMnjqaAPnTwp+y1a2/gDw94n1rSLmHx5o/gB/Dp01bqI2z3v2WWKORypKPKizzRK+8qFkPPAIAMbUvh78cNL074b6HdeE/Fmt+HNH8A6fpN1ofh7xbFo3k65GirKbuaOVGli2KqhkdlUqSFO75gCf4e/BP4n6J4a+ElhqvhnyLjwx4R8T6Xqqfbbdvs9zd+V9njyshD7trcrkDHzEUAdToXwr8c2HhT9nvT5NBEV54GlhOvKtzDmzX+zJoZOQ+JP3jqp8stnOenNAH0DQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB//9k=" + } + } + ] + } + ], + "starredVariables": [], + "dataTableConfig": {}, + "derivedVariables": [] + } +} \ No newline at end of file From 11b5e8492c023a87d9de9af024939e2e3230b0e8 Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Wed, 10 Apr 2024 12:56:17 -0400 Subject: [PATCH 03/16] Add comments --- .../edaanalysis/plugins/VDIEntityIdRetriever.java | 5 +++-- .../edaanalysis/plugins/VDIMigrationPlugin.java | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java index 506c01832..1443411e4 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java @@ -3,6 +3,7 @@ import org.gusdb.fgputil.db.runner.SQLRunner; import javax.sql.DataSource; +import java.util.Optional; public class VDIEntityIdRetriever { private DataSource eda; @@ -11,14 +12,14 @@ public VDIEntityIdRetriever(DataSource eda) { this.eda = eda; } - public String queryEntityId(String vdiStableId) { + public Optional queryEntityId(String vdiStableId) { final String sql = "SELECT internal_abbrev FROM userstudydatasetid u" + "JOIN vdi_datasets_dev_s.entitytypegraph etg" + "ON u.study_stable_id = etg.study_stable_id" + "WHERE dataset_stable_id = ?"; return new SQLRunner(eda, sql).executeQuery(new Object[] { vdiStableId }, rs -> { rs.next(); - return rs.getString("internal_abbrev"); + return Optional.ofNullable(rs.getString("internal_abbrev")); }); } } diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java index a63dbde9d..20e60cb56 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -19,6 +19,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -72,13 +73,19 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR if (vdiId == null) { LOG.warn("Unable to find legacy ID " + legacyUdId + " in the tinydb file."); missingFromVdiCount++; - return new TableRowInterfaces.RowResult<>(nextRow); + return new TableRowInterfaces.RowResult<>(nextRow) + .setShouldWrite(false); } // Append UD prefix to VDI ID. The prefix is prepended in the view that maps stable VDI IDs to the unstable study // ID, which is the currency of EDA. final String vdiDatasetId = UD_DATASET_ID_PREFIX + vdiId; - final String vdiEntityId = _vdiEntityIdRetriever.queryEntityId(vdiDatasetId); + final Optional vdiEntityId = _vdiEntityIdRetriever.queryEntityId(vdiDatasetId); + if (!vdiEntityId.isPresent()) { + LOG.warn("Unable to find entity ID in appdb for VDI dataset ID: " + vdiDatasetId); + return new TableRowInterfaces.RowResult<>(nextRow) + .setShouldWrite(false); + } String descriptor = nextRow.getDescriptor().toString(); @@ -92,10 +99,12 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR .map(m -> m.group(1)) .orElse(null); + // Replace all entityID with entityID looked up from database. if (entityId != null) { - descriptor = descriptor.replaceAll(entityId, vdiEntityId); + descriptor = descriptor.replaceAll(entityId, vdiEntityId.get()); } + // Replace all variable IDs with value converted from legacy variable ID. for (String legacyVariableId: legacyVariableIds) { descriptor = descriptor.replaceAll(legacyVariableId, convertToVdiId(legacyVariableId)); } From f6b6aa6b5f5259e342f5057de6d48d944b0a35a3 Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Wed, 10 Apr 2024 12:58:04 -0400 Subject: [PATCH 04/16] Read dry-run property --- .../fix/table/edaanalysis/plugins/VDIMigrationPlugin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java index 20e60cb56..a085e527a 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -113,7 +113,8 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR AnalysisRow out = new AnalysisRow(nextRow.getAnalysisId(), vdiDatasetId, new JSONObject(descriptor), nextRow.getNumFilters(), nextRow.getNumComputations(), nextRow.getNumVisualizations()); - return new TableRowInterfaces.RowResult<>(out); + return new TableRowInterfaces.RowResult<>(out) + .setShouldWrite(_writeToDb); } private String convertToVdiId(String legacyVariableId) { From 93a6ac10fb4f4b22a079fa5dedd6bf8ec15e58a5 Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Wed, 10 Apr 2024 13:10:04 -0400 Subject: [PATCH 05/16] Fix test --- .../fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java b/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java index 4842ab345..69f1826df 100644 --- a/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java +++ b/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java @@ -17,6 +17,7 @@ import java.nio.file.Path; import java.util.List; import java.util.Objects; +import java.util.Optional; public class VDIMigrationPluginTest { private WdkModel mockedModel; @@ -37,7 +38,7 @@ public void test() throws Exception { final File file = new File(Objects.requireNonNull(classLoader.getResource("migration-unit-test-1.json")).getFile()); final VDIMigrationPlugin migrationPlugin = new VDIMigrationPlugin(); final List args = List.of("--tinyDb=" + file.getPath()); - Mockito.when(retriever.queryEntityId("EDAUD_123XyZ")).thenReturn("EDAUD_Migrated_ID"); + Mockito.when(retriever.queryEntityId("EDAUD_123XyZ")).thenReturn(Optional.of("EDAUD_Migrated_ID")); migrationPlugin.configure(mockedModel, args, retriever); TableRowInterfaces.RowResult result = migrationPlugin.processRecord( new AnalysisRow("x", From d339e5a9bb2e6b971833380c712feb9a86d45cbb Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Wed, 10 Apr 2024 15:00:39 -0400 Subject: [PATCH 06/16] Fixes for dry run --- .../plugins/VDIEntityIdRetriever.java | 10 ++++--- .../plugins/VDIMigrationPlugin.java | 30 ++++++++++++------- .../plugins/VDIMigrationPluginTest.java | 4 +-- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java index 1443411e4..e2248d33f 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java @@ -7,16 +7,18 @@ public class VDIEntityIdRetriever { private DataSource eda; + private String schema; - public VDIEntityIdRetriever(DataSource eda) { + public VDIEntityIdRetriever(DataSource eda, String schema) { this.eda = eda; + this.schema = schema; } public Optional queryEntityId(String vdiStableId) { - final String sql = "SELECT internal_abbrev FROM userstudydatasetid u" + - "JOIN vdi_datasets_dev_s.entitytypegraph etg" + + final String sql = String.format("SELECT internal_abbrev FROM userstudydatasetid u" + + "JOIN %s.entitytypegraph etg" + "ON u.study_stable_id = etg.study_stable_id" + - "WHERE dataset_stable_id = ?"; + "WHERE dataset_stable_id = ?", schema); return new SQLRunner(eda, sql).executeQuery(new Object[] { vdiStableId }, rs -> { rs.next(); return Optional.ofNullable(rs.getString("internal_abbrev")); diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java index a085e527a..099d78671 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -36,11 +36,6 @@ public class VDIMigrationPlugin extends AbstractAnalysisUpdater { @Override public void configure(WdkModel wdkModel, List additionalArgs) throws Exception { - configure(wdkModel, additionalArgs, new VDIEntityIdRetriever(wdkModel.getAppDb().getDataSource())); - } - - // Visible for testing. - void configure(WdkModel wdkModel, List additionalArgs, VDIEntityIdRetriever entityIdRetriever) { // Parse args in the format --= final Map args = additionalArgs.stream() .map(arg -> Arrays.stream(arg.split("=")) @@ -50,20 +45,35 @@ void configure(WdkModel wdkModel, List additionalArgs, VDIEntityIdRetrie pair -> pair.get(0), pair -> pair.size() > 1 ? pair.get(1) : "true")); // A flag without an "=" is a boolean. Set true if present. - // Validate required arg. + // Validate required args. if (!args.containsKey("--tinyDb")) { throw new IllegalArgumentException("Missing required flag --tinyDb"); } - final File tinyDbFile = new File(args.get("--tinyDb")); + if (!args.containsKey(("--schema"))) { + throw new IllegalArgumentException("Missing required argument --schema"); + } + + final String schema = args.get("--schema"); + setEntityIdRetriever(new VDIEntityIdRetriever(wdkModel.getAppDb().getDataSource(), schema)); - _legacyIdToVdiId = readLegacyStudyIdToVdiId(tinyDbFile); + final File tinyDbFile = new File(args.get("--tinyDb")); + readVdiMappingFile(tinyDbFile); // Default to dryrun to avoid incidental migrations when testing. - _writeToDb = Boolean.parseBoolean(args.getOrDefault("--liveRun", "false")); + _writeToDb = Boolean.parseBoolean(args.getOrDefault("-write", "false")); _wdkModel = wdkModel; + } + + // Visible for testing. + void setEntityIdRetriever(VDIEntityIdRetriever entityIdRetriever) { _vdiEntityIdRetriever = entityIdRetriever; } + // Visible for testing + void readVdiMappingFile(File mappingFile) { + _legacyIdToVdiId = readLegacyStudyIdToVdiId(mappingFile); + } + @Override public TableRowInterfaces.RowResult processRecord(AnalysisRow nextRow) throws Exception { final String legacyDatasetId = nextRow.getDatasetId(); @@ -81,7 +91,7 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR // ID, which is the currency of EDA. final String vdiDatasetId = UD_DATASET_ID_PREFIX + vdiId; final Optional vdiEntityId = _vdiEntityIdRetriever.queryEntityId(vdiDatasetId); - if (!vdiEntityId.isPresent()) { + if (vdiEntityId.isEmpty()) { LOG.warn("Unable to find entity ID in appdb for VDI dataset ID: " + vdiDatasetId); return new TableRowInterfaces.RowResult<>(nextRow) .setShouldWrite(false); diff --git a/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java b/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java index 69f1826df..150931baa 100644 --- a/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java +++ b/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java @@ -37,9 +37,9 @@ public void test() throws Exception { JSONObject descriptor = new JSONObject(Files.readString(Path.of(analysisFile.getPath()))); final File file = new File(Objects.requireNonNull(classLoader.getResource("migration-unit-test-1.json")).getFile()); final VDIMigrationPlugin migrationPlugin = new VDIMigrationPlugin(); - final List args = List.of("--tinyDb=" + file.getPath()); + migrationPlugin.readVdiMappingFile(file); Mockito.when(retriever.queryEntityId("EDAUD_123XyZ")).thenReturn(Optional.of("EDAUD_Migrated_ID")); - migrationPlugin.configure(mockedModel, args, retriever); + migrationPlugin.setEntityIdRetriever(retriever); TableRowInterfaces.RowResult result = migrationPlugin.processRecord( new AnalysisRow("x", "EDAUD_1234", From 3ca62b1f47389b5328540a332d307161dbe2b52d Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Wed, 10 Apr 2024 15:27:47 -0400 Subject: [PATCH 07/16] Add file reader --- .../wdk/model/fix/VdiMigrationFileReader.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Model/src/main/java/org/gusdb/wdk/model/fix/VdiMigrationFileReader.java diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/VdiMigrationFileReader.java b/Model/src/main/java/org/gusdb/wdk/model/fix/VdiMigrationFileReader.java new file mode 100644 index 000000000..230458212 --- /dev/null +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/VdiMigrationFileReader.java @@ -0,0 +1,57 @@ +package org.gusdb.wdk.model.fix; + +import com.fasterxml.jackson.databind.JsonNode; +import org.gusdb.fgputil.json.JsonUtil; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public class VdiMigrationFileReader { + private File file; + + public VdiMigrationFileReader(File file) { + this.file = file; + } + + /** + * Parse the tinydb file into a map of legacy UD identifiers to VDI identifiers. + * + * Example file format: + * + * { + * "_default": { + * "1": { + * "type": "owner", + * "udId": 1234, + * "vdiId": "123XyZ", + * "msg": null, + * "time": "Fri Mar 26 00:00:00 2024" + * } + * } + * + * @return Map of legacy UD Ids to VDI Ids. + */ + public Map readLegacyStudyIdToVdiId() { + try { + JsonNode root = JsonUtil.Jackson.readTree(file); + JsonNode dbRoot = root.get("_default"); + + Map mapping = new HashMap<>(); + Iterator> fieldIterator = dbRoot.fields(); + + // Iterate through each field in the "_default" node. + // Ignore the numeric index keys and extract the udId and vdiId fields to create mapping. + while (fieldIterator.hasNext()) { + Map.Entry entry = fieldIterator.next(); + mapping.put(entry.getValue().get("udId").asText(), entry.getValue().get("vdiId").asText()); + } + + return mapping; + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file From 36128305f30fb8e7ce885eab4e913225a7203146 Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Thu, 11 Apr 2024 12:54:24 -0400 Subject: [PATCH 08/16] Fix merge conflicts --- .../wdk/model/fix/VdiMigrationFileReader.java | 4 -- .../plugins/VDIMigrationPluginTest.java | 51 ++++--------------- 2 files changed, 9 insertions(+), 46 deletions(-) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/VdiMigrationFileReader.java b/Model/src/main/java/org/gusdb/wdk/model/fix/VdiMigrationFileReader.java index 8dcec6e61..d4578d59f 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/VdiMigrationFileReader.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/VdiMigrationFileReader.java @@ -54,8 +54,4 @@ public Map readLegacyStudyIdToVdiId() { throw new RuntimeException(e); } } -<<<<<<< HEAD } -======= -} ->>>>>>> master diff --git a/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java b/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java index dddec078f..0a039a90d 100644 --- a/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java +++ b/Model/src/test/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPluginTest.java @@ -3,101 +3,68 @@ import org.gusdb.wdk.model.WdkModel; import org.gusdb.wdk.model.fix.table.TableRowInterfaces; import org.gusdb.wdk.model.fix.table.edaanalysis.AnalysisRow; -<<<<<<< HEAD -import org.hamcrest.MatcherAssert; -======= ->>>>>>> master import org.json.JSONObject; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -<<<<<<< HEAD -import org.mockito.Matchers; import org.mockito.Mockito; import java.io.File; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; import java.util.Objects; import java.util.Optional; -======= -import org.mockito.Mockito; - -import java.io.File; -import java.util.List; -import java.util.Objects; ->>>>>>> master public class VDIMigrationPluginTest { private WdkModel mockedModel; private ClassLoader classLoader; -<<<<<<< HEAD private VDIEntityIdRetriever retriever; -======= ->>>>>>> master @Before public void setup() { classLoader = getClass().getClassLoader(); mockedModel = Mockito.mock(WdkModel.class); -<<<<<<< HEAD retriever = Mockito.mock(VDIEntityIdRetriever.class); } @Test - public void test() throws Exception { + public void testUpdateEnabled() throws Exception { File analysisFile = new File(Objects.requireNonNull(classLoader.getResource("analysis-unit-test-1.json")).getFile()); JSONObject descriptor = new JSONObject(Files.readString(Path.of(analysisFile.getPath()))); final File file = new File(Objects.requireNonNull(classLoader.getResource("migration-unit-test-1.json")).getFile()); final VDIMigrationPlugin migrationPlugin = new VDIMigrationPlugin(); + Mockito.when(retriever.queryEntityId("EDAUD_123XyZ")).thenReturn(Optional.of("asdf")); migrationPlugin.readVdiMappingFile(file); - Mockito.when(retriever.queryEntityId("EDAUD_123XyZ")).thenReturn(Optional.of("EDAUD_Migrated_ID")); migrationPlugin.setEntityIdRetriever(retriever); TableRowInterfaces.RowResult result = migrationPlugin.processRecord( new AnalysisRow("x", "EDAUD_1234", descriptor, -======= - } - - @Test - public void testUpdateEnabled() throws Exception { - final File file = new File(Objects.requireNonNull(classLoader.getResource("migration-unit-test-1.json")).getFile()); - final VDIMigrationPlugin migrationPlugin = new VDIMigrationPlugin(); - final List args = List.of("--tinyDb=" + file.getPath()); - migrationPlugin.configure(mockedModel, args); - TableRowInterfaces.RowResult result = migrationPlugin.processRecord( - new AnalysisRow("x", - "EDAUD_1234", - new JSONObject(), ->>>>>>> master 3, 4, 5)); Assert.assertEquals("EDAUD_123XyZ", result.getRow().getDatasetId()); -<<<<<<< HEAD Assert.assertTrue(result.getRow().getDescriptor().toString().contains("VAR_c73e53adb951e2fe")); -======= Assert.assertFalse(result.shouldWrite()); } @Test public void testUpdateDisabled() throws Exception { + File analysisFile = new File(Objects.requireNonNull(classLoader.getResource("analysis-unit-test-1.json")).getFile()); + JSONObject descriptor = new JSONObject(Files.readString(Path.of(analysisFile.getPath()))); final File file = new File(Objects.requireNonNull(classLoader.getResource("migration-unit-test-1.json")).getFile()); final VDIMigrationPlugin migrationPlugin = new VDIMigrationPlugin(); - final List args = List.of("--tinyDb=" + file.getPath(), "--liveRun"); - migrationPlugin.configure(mockedModel, args); + Mockito.when(retriever.queryEntityId("EDAUD_123XyZ")).thenReturn(Optional.of("asdf")); + migrationPlugin.readVdiMappingFile(file); + migrationPlugin.setEntityIdRetriever(retriever); TableRowInterfaces.RowResult result = migrationPlugin.processRecord( new AnalysisRow("x", "EDAUD_1234", - new JSONObject(), + descriptor, 3, 4, 5)); Assert.assertEquals("EDAUD_123XyZ", result.getRow().getDatasetId()); - Assert.assertTrue(result.shouldWrite()); ->>>>>>> master + Assert.assertFalse(result.shouldWrite()); } } From 9eba1c8f2332ede86c6702036252e5ba6cf9a51a Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Thu, 11 Apr 2024 13:06:09 -0400 Subject: [PATCH 09/16] Add logging --- .../fix/table/edaanalysis/plugins/VDIMigrationPlugin.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java index 97155335b..36588ca3a 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -102,7 +102,8 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR return new TableRowInterfaces.RowResult<>(nextRow) .setShouldWrite(false); } - + + LOG.info("Analysis descriptor before migration: " + nextRow.getDescriptor()); String descriptor = nextRow.getDescriptor().toString(); // Find all variable IDs. @@ -129,6 +130,8 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR AnalysisRow out = new AnalysisRow(nextRow.getAnalysisId(), vdiDatasetId, new JSONObject(descriptor), nextRow.getNumFilters(), nextRow.getNumComputations(), nextRow.getNumVisualizations()); + LOG.info("Analysis descriptor after migration: " + out.getDescriptor()); + return new TableRowInterfaces.RowResult<>(out) .setShouldWrite(_writeToDb); } From b6cabcff6267b51fcd6bfed3fca686d03c982192 Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Tue, 16 Apr 2024 15:16:26 -0400 Subject: [PATCH 10/16] Fix plugin --- .../table/edaanalysis/plugins/VDIEntityIdRetriever.java | 8 ++++---- .../fix/table/edaanalysis/plugins/VDIMigrationPlugin.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java index e2248d33f..7aab37d0c 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java @@ -15,10 +15,10 @@ public VDIEntityIdRetriever(DataSource eda, String schema) { } public Optional queryEntityId(String vdiStableId) { - final String sql = String.format("SELECT internal_abbrev FROM userstudydatasetid u" + - "JOIN %s.entitytypegraph etg" + - "ON u.study_stable_id = etg.study_stable_id" + - "WHERE dataset_stable_id = ?", schema); + final String sql = String.format("SELECT internal_abbrev FROM %s.userstudydatasetid u" + + " JOIN %s.entitytypegraph etg" + + " ON u.study_stable_id = etg.study_stable_id" + + " WHERE dataset_stable_id = ?", schema, schema); return new SQLRunner(eda, sql).executeQuery(new Object[] { vdiStableId }, rs -> { rs.next(); return Optional.ofNullable(rs.getString("internal_abbrev")); diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java index 36588ca3a..43684c4e2 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -102,7 +102,7 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR return new TableRowInterfaces.RowResult<>(nextRow) .setShouldWrite(false); } - + LOG.info("Analysis descriptor before migration: " + nextRow.getDescriptor()); String descriptor = nextRow.getDescriptor().toString(); From 20646302290a20d2c22edbc8a5b0fe4e192779d6 Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Tue, 16 Apr 2024 16:10:39 -0400 Subject: [PATCH 11/16] Fix NPE --- .../fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java index 7aab37d0c..7ae0e375f 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIEntityIdRetriever.java @@ -20,7 +20,10 @@ public Optional queryEntityId(String vdiStableId) { " ON u.study_stable_id = etg.study_stable_id" + " WHERE dataset_stable_id = ?", schema, schema); return new SQLRunner(eda, sql).executeQuery(new Object[] { vdiStableId }, rs -> { - rs.next(); + boolean hasNext = rs.next(); + if (!hasNext) { + return Optional.empty(); + } return Optional.ofNullable(rs.getString("internal_abbrev")); }); } From b714293b937329dd1f0dd45090c02236dcd33e91 Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Thu, 18 Apr 2024 18:38:15 -0400 Subject: [PATCH 12/16] Test without thumbnail --- .../fix/table/edaanalysis/plugins/VDIMigrationPlugin.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java index 43684c4e2..ff57a6aaa 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -130,7 +130,12 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR AnalysisRow out = new AnalysisRow(nextRow.getAnalysisId(), vdiDatasetId, new JSONObject(descriptor), nextRow.getNumFilters(), nextRow.getNumComputations(), nextRow.getNumVisualizations()); - LOG.info("Analysis descriptor after migration: " + out.getDescriptor()); + // TODO REMOVE BELOW + JSONObject descriptorWithoutThumbnail = out.getDescriptor(); + descriptorWithoutThumbnail.remove("thumbnail"); + // TODO REMOVE ABOVE + + LOG.info("Analysis descriptor after migration: " + descriptorWithoutThumbnail); return new TableRowInterfaces.RowResult<>(out) .setShouldWrite(_writeToDb); From 33b371f262a8a5ae405a0486cdf1b1560be91bff Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Thu, 18 Apr 2024 18:47:00 -0400 Subject: [PATCH 13/16] Remove extra logging --- .../table/edaanalysis/plugins/VDIMigrationPlugin.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java index ff57a6aaa..841140524 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -129,13 +129,8 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR // Create a copy with just the dataset ID updated to VDI counterpart. AnalysisRow out = new AnalysisRow(nextRow.getAnalysisId(), vdiDatasetId, new JSONObject(descriptor), nextRow.getNumFilters(), nextRow.getNumComputations(), nextRow.getNumVisualizations()); - - // TODO REMOVE BELOW - JSONObject descriptorWithoutThumbnail = out.getDescriptor(); - descriptorWithoutThumbnail.remove("thumbnail"); - // TODO REMOVE ABOVE - - LOG.info("Analysis descriptor after migration: " + descriptorWithoutThumbnail); + + LOG.info("Analysis descriptor after migration: " + out); return new TableRowInterfaces.RowResult<>(out) .setShouldWrite(_writeToDb); From 9a51814ed6546a4c7cc76e688b0aaa32a4781921 Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Mon, 22 Apr 2024 13:24:36 -0400 Subject: [PATCH 14/16] Address comments --- .../table/edaanalysis/plugins/VDIMigrationPlugin.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java index 841140524..3860e55bd 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -127,12 +127,12 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR } // Create a copy with just the dataset ID updated to VDI counterpart. - AnalysisRow out = new AnalysisRow(nextRow.getAnalysisId(), vdiDatasetId, new JSONObject(descriptor), - nextRow.getNumFilters(), nextRow.getNumComputations(), nextRow.getNumVisualizations()); - - LOG.info("Analysis descriptor after migration: " + out); + nextRow.setDescriptor(new JSONObject(descriptor)); + nextRow.setDatasetId(vdiDatasetId); - return new TableRowInterfaces.RowResult<>(out) + LOG.info("Analysis descriptor after migration: " + descriptor); + + return new TableRowInterfaces.RowResult<>(nextRow) .setShouldWrite(_writeToDb); } From 6fcf69ca4b044058bca17f3c1b2bcf3146706700 Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Mon, 22 Apr 2024 13:25:34 -0400 Subject: [PATCH 15/16] Consistent arg formatting --- .../model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java index 3860e55bd..4f0fc47bc 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -61,7 +61,7 @@ public void configure(WdkModel wdkModel, List additionalArgs) throws Exc readVdiMappingFile(tinyDbFile); // Default to dryrun to avoid incidental migrations when testing. - _writeToDb = Boolean.parseBoolean(args.getOrDefault("-write", "false")); + _writeToDb = Boolean.parseBoolean(args.getOrDefault("--write", "false")); _wdkModel = wdkModel; } From 2d99a781fa5b0aee69b2fed5b4a256224f5a40d3 Mon Sep 17 00:00:00 2001 From: Dan Galdi Date: Tue, 23 Apr 2024 13:26:59 -0400 Subject: [PATCH 16/16] Fix migration plugin to use manual ID overrides --- .../edaanalysis/plugins/VDIMigrationPlugin.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java index 4f0fc47bc..18442dd77 100644 --- a/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java +++ b/Model/src/main/java/org/gusdb/wdk/model/fix/table/edaanalysis/plugins/VDIMigrationPlugin.java @@ -30,6 +30,15 @@ public class VDIMigrationPlugin extends AbstractAnalysisUpdater { private static final String UD_DATASET_ID_PREFIX = "EDAUD_"; private static final Pattern VAR_ID_PATTERN = Pattern.compile("variableId\":\\s*\"([a-zA-Z0-9_-]+)"); private static final Pattern ENTITY_ID_PATTERN = Pattern.compile("entityId\":\\s*\"([a-zA-Z0-9_-]+)"); + private static final Map VAR_ID_MAPPING_OVERRIDE = Map.of( + // Latitude mappings + "VAR_13DCE851F0DDECBE", "OBI_0001620", + "VAR_4A934C04C995BF7B", "OBI_0001620", + "VAR_F3074604E6180BE6", "OBI_0001620", + // Longitude mappings + "VAR_44452C5F22B37BB", "OBI_0001621", + "VAR_86723E25E8EE8FD8", "OBI_0001621" + ); private Map _legacyIdToVdiId; private VDIEntityIdRetriever _vdiEntityIdRetriever; @@ -137,6 +146,9 @@ public TableRowInterfaces.RowResult processRecord(AnalysisRow nextR } private String convertToVdiId(String legacyVariableId) { + if (VAR_ID_MAPPING_OVERRIDE.containsKey(legacyVariableId)) { + return VAR_ID_MAPPING_OVERRIDE.get(legacyVariableId); + } byte[] encodedId = DigestUtils.digest(DigestUtils.getSha1Digest(), legacyVariableId.getBytes(StandardCharsets.UTF_8)); return "VAR_" + Hex.encodeHexString(encodedId).substring(0, 16); }