Skip to content

Commit fe8ed0a

Browse files
EES-4945 - implemented automapping and tests
1 parent 7192381 commit fe8ed0a

File tree

7 files changed

+1200
-110
lines changed

7 files changed

+1200
-110
lines changed

src/GovUk.Education.ExploreEducationStatistics.Public.Data.Model/DataSetVersionImportStage.cs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public enum DataSetVersionImportStage
1010
CopyingCsvFiles,
1111
ImportingMetadata,
1212
CreatingMappings,
13+
AutoMapping,
1314
ImportingData,
1415
WritingDataFiles,
1516
Completing

src/GovUk.Education.ExploreEducationStatistics.Public.Data.Model/DataSetVersionMapping.cs

+81-53
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ public class DataSetVersionMapping : ICreatedUpdatedTimestamps<DateTimeOffset, D
2424
public FilterMappingPlan FilterMappingPlan { get; set; } = null!;
2525

2626
public bool LocationMappingsComplete { get; set; }
27-
27+
2828
public bool FilterMappingsComplete { get; set; }
29-
29+
3030
public DateTimeOffset Created { get; set; }
3131

3232
public DateTimeOffset? Updated { get; set; }
@@ -38,18 +38,18 @@ public void Configure(EntityTypeBuilder<DataSetVersionMapping> builder)
3838
builder.Property(mapping => mapping.Id)
3939
.HasValueGenerator<UuidV7ValueGenerator>();
4040

41-
builder.HasIndex(mapping => new { mapping.SourceDataSetVersionId })
41+
builder.HasIndex(mapping => new {mapping.SourceDataSetVersionId})
4242
.HasDatabaseName("IX_DataSetVersionMappings_SourceDataSetVersionId")
4343
.IsUnique();
4444

45-
builder.HasIndex(mapping => new { mapping.TargetDataSetVersionId })
45+
builder.HasIndex(mapping => new {mapping.TargetDataSetVersionId})
4646
.HasDatabaseName("IX_DataSetVersionMappings_TargetDataSetVersionId")
4747
.IsUnique();
4848

4949
builder.OwnsOne(v => v.LocationMappingPlan, locations =>
5050
{
5151
locations.ToJson();
52-
locations.OwnsMany(l => l.Mappings, locationMappings =>
52+
locations.OwnsMany(l => l.Levels, locationMappings =>
5353
{
5454
locationMappings.OwnsMany(mapping => mapping.Mappings, locationMapping =>
5555
{
@@ -59,9 +59,10 @@ public void Configure(EntityTypeBuilder<DataSetVersionMapping> builder)
5959
});
6060
});
6161

62-
locations.OwnsMany(locations => locations.Candidates, locationTargets =>
62+
locations.OwnsMany(location => location.Levels, level =>
6363
{
64-
locationTargets.OwnsMany(mapping => mapping.Candidates);
64+
level.OwnsMany(mapping => mapping.Mappings);
65+
level.OwnsMany(mapping => mapping.Candidates);
6566
});
6667
});
6768

@@ -75,7 +76,7 @@ public void Configure(EntityTypeBuilder<DataSetVersionMapping> builder)
7576
filterMapping.Property(mapping => mapping.Type)
7677
.HasConversion(new EnumToEnumValueConverter<MappingType>());
7778

78-
filterMapping.OwnsMany(mapping => mapping.Options, filterOptionMapping =>
79+
filterMapping.OwnsMany(mapping => mapping.OptionMappings, filterOptionMapping =>
7980
{
8081
filterOptionMapping.OwnsOne(mapping => mapping.Source);
8182
filterOptionMapping.Property(mapping => mapping.Type)
@@ -107,7 +108,7 @@ public enum MappingType
107108
/// The user has manually chosen a mapping candidate for this source element.
108109
/// </summary>
109110
ManualMapped,
110-
111+
111112
/// <summary>
112113
/// The user has manually indicated that no mapping candidate exists for this source element.
113114
/// </summary>
@@ -117,7 +118,7 @@ public enum MappingType
117118
/// The server has automatically selected a likely mapping candidate for this source element.
118119
/// </summary>
119120
AutoMapped,
120-
121+
121122
/// <summary>
122123
/// The server has automatically indicated that no likely mapping candidate exists for this source element.
123124
/// </summary>
@@ -127,13 +128,20 @@ public enum MappingType
127128
/// <summary>
128129
/// This base class represents an element from the DataSetVersions that can be mapped.
129130
/// </summary>
130-
public abstract class MappableElement
131+
public abstract record MappableElement
131132
{
132133
/// <summary>
133134
/// This is a synthetic identifier which is used to identify source and target
134135
/// elements in lieu of using actual database ids.
135136
/// </summary>
136-
public string Key { get; set; }
137+
public string Key { get; set; } = string.Empty;
138+
}
139+
140+
public abstract record MappableElementWithOptions<TMappableOption>
141+
: MappableElement
142+
where TMappableOption : MappableElement
143+
{
144+
public List<TMappableOption> Options { get; set; } = [];
137145
}
138146

139147
/// <summary>
@@ -142,90 +150,110 @@ public abstract class MappableElement
142150
/// the type of mapping that has been performed (e.g. the user choosing a candidate Location from
143151
/// the target DataSetVersion) and the candidate key (if a candidate has been chosen).
144152
/// </summary>
145-
public abstract class LeafMapping<TMappableElement>
146-
where TMappableElement : MappableElement
153+
public abstract record Mapping<TMappableElement>
154+
where TMappableElement : class
147155
{
148-
public MappingType Type { get; set; }
156+
public MappingType Type { get; set; } = MappingType.None;
149157

150158
public TMappableElement Source { get; set; } = null!;
151159

152160
public string? CandidateKey { get; set; }
153161
}
154162

155-
public abstract class ParentMapping<TMappableElement, TOption, TOptionMapping>
163+
/// <summary>
164+
/// This base class represents a mapping for a parent element which then itself also contains
165+
/// child elements (or "options") that can themselves be mapped.
166+
/// </summary>
167+
public abstract record ParentMapping<TMappableElement, TOption, TOptionMapping>
168+
: Mapping<TMappableElement>
156169
where TMappableElement : MappableElement
157170
where TOption : MappableElement
158-
where TOptionMapping : LeafMapping<TOption>
171+
where TOptionMapping : Mapping<TOption>
159172
{
160-
public MappingType Type { get; set; }
161-
162-
public TMappableElement Source { get; set; } = null!;
163-
164-
public List<TOptionMapping> Options { get; set; } = [];
165-
166-
public string? CandidateKey { get; set; }
173+
public List<TOptionMapping> OptionMappings { get; set; } = [];
167174
}
168175

169-
public class LocationOption : MappableElement
176+
/// <summary>
177+
/// This represents a location option that is potentially mappable to another location option
178+
/// from the same geographic level.
179+
/// </summary>
180+
public record LocationOption : MappableElement
170181
{
171182
public string Label { get; set; } = string.Empty;
172-
173-
protected string? Code { get; set; }
174-
175-
protected string? OldCode { get; set; }
176-
177-
protected string? Urn { get; set; }
178-
179-
protected string? LaEstab { get; set; }
180-
181-
protected string? Ukprn { get; set; }
182183
}
183184

184-
public class LocationOptionMapping : LeafMapping<LocationOption>;
185+
/// <summary>
186+
/// This represents the mapping, or failure to map, of a source location option to a target
187+
/// location option from the same geographic level.
188+
/// </summary>
189+
public record LocationOptionMapping : Mapping<LocationOption>;
185190

186-
public class LocationLevelMappingPlan
191+
/// <summary>
192+
/// This represents a single geographic level's worth of location mappings from the source
193+
/// data set version and potential candidates to map to from the target data set version.
194+
/// </summary>
195+
public record LocationLevelMappings
187196
{
188197
public GeographicLevel Level { get; set; }
189198

190199
public List<LocationOptionMapping> Mappings { get; set; } = [];
191-
}
192-
193-
public class LocationLevelMappingCandidates
194-
{
195-
public GeographicLevel Level { get; set; }
196200

197201
public List<LocationOption> Candidates { get; set; } = [];
198202
}
199203

204+
/// <summary>
205+
/// This represents the overall mapping plan for all the geographic levels
206+
/// and locations from the source data set version to the target version.
207+
/// </summary>
200208
public class LocationMappingPlan
201209
{
202-
public List<LocationLevelMappingPlan> Mappings { get; set; }
203-
204-
public List<LocationLevelMappingCandidates> Candidates { get; set; }
210+
public List<LocationLevelMappings> Levels { get; set; } = [];
205211
}
206212

207-
public class FilterOption : MappableElement
213+
/// <summary>
214+
/// This represents a filter option that is potentially mappable to another filter option.
215+
/// </summary>
216+
public record FilterOption : MappableElement
208217
{
209218
public string Label { get; set; } = string.Empty;
210219
}
211220

212-
public class Filter : MappableElement
221+
/// <summary>
222+
/// This represents a filter that is potentially mappable to another filter.
223+
/// </summary>
224+
public record Filter : MappableElement
213225
{
214226
public string Label { get; set; } = string.Empty;
215227
}
216228

217-
public class FilterMappingCandidate : MappableElement
229+
/// <summary>
230+
/// This represents a candidate filter and all of its candidate filter options from
231+
/// the target data set version that could be mapped to from filters and filter options
232+
/// from the source version.
233+
/// </summary>
234+
public record FilterMappingCandidate : MappableElementWithOptions<FilterOption>
218235
{
219236
public string Label { get; set; } = string.Empty;
220-
221-
public List<FilterOption> Options { get; set; } = [];
222237
}
223238

224-
public class FilterOptionMapping : LeafMapping<FilterOption>;
239+
/// <summary>
240+
/// This represents a potential mapping of a filter option from the source data set version
241+
/// to a filter option in the target version. In order to be mappable, both filter options'
242+
/// parent filters must firstly be mapped to each other.
243+
/// </summary>
244+
public record FilterOptionMapping : Mapping<FilterOption>;
225245

226-
public class FilterMapping : ParentMapping<Filter, FilterOption, FilterOptionMapping>;
246+
/// <summary>
247+
/// This represents a potential mapping of a filter from the source data set version
248+
/// to a filter in the target version.
249+
/// </summary>
250+
public record FilterMapping : ParentMapping<Filter, FilterOption, FilterOptionMapping>;
227251

228-
public class FilterMappingPlan
252+
/// <summary>
253+
/// This represents the overall mapping plan for filters and filter options from the source
254+
/// data set version to filters and filter options in the target data set version.
255+
/// </summary>
256+
public record FilterMappingPlan
229257
{
230258
public List<FilterMapping> Mappings { get; set; } = [];
231259

0 commit comments

Comments
 (0)