Skip to content

Commit 85e6226

Browse files
committed
Getting started with import column mapping
1 parent 1f23431 commit 85e6226

10 files changed

Lines changed: 249 additions & 113 deletions

File tree

src/Libraries/SmartStore.Services/DataExchange/ColumnMap.cs

Lines changed: 0 additions & 95 deletions
This file was deleted.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace SmartStore.Services.DataExchange.Import
5+
{
6+
public class ColumnMap
7+
{
8+
private readonly Dictionary<string, ColumnMappingValue> _map = new Dictionary<string, ColumnMappingValue>(StringComparer.OrdinalIgnoreCase);
9+
10+
public IReadOnlyDictionary<string, ColumnMappingValue> Mappings
11+
{
12+
get { return _map; }
13+
}
14+
15+
public void AddMapping(string sourceColumn, string entityProperty, string defaultValue = null)
16+
{
17+
AddMapping(sourceColumn, null, entityProperty, defaultValue);
18+
}
19+
20+
public void AddMapping(string sourceColumn, string index, string entityProperty, string defaultValue = null)
21+
{
22+
Guard.ArgumentNotEmpty(() => sourceColumn);
23+
Guard.ArgumentNotEmpty(() => entityProperty);
24+
25+
_map[CreateSourceName(sourceColumn, index)] = new ColumnMappingValue
26+
{
27+
EntityProperty = entityProperty,
28+
DefaultValue = defaultValue
29+
};
30+
}
31+
32+
/// <summary>
33+
/// Gets a mapped column value
34+
/// </summary>
35+
/// <param name="sourceColumn">The name of the column to get a mapped value for.</param>
36+
/// <returns>The mapped column value OR - if the name is unmapped - a value with the passed <paramref name="sourceColumn"/></returns>
37+
public ColumnMappingValue GetMapping(string sourceColumn)
38+
{
39+
ColumnMappingValue result;
40+
41+
if (_map.TryGetValue(sourceColumn, out result))
42+
{
43+
return result;
44+
}
45+
46+
return new ColumnMappingValue { EntityProperty = sourceColumn };
47+
}
48+
49+
/// <summary>
50+
/// Gets a mapped column value
51+
/// </summary>
52+
/// <param name="sourceColumn">The name of the column to get a mapped value for.</param>
53+
/// <param name="index">The column index, e.g. a language code (de, en etc.)</param>
54+
/// <returns>The mapped column value OR - if the name is unmapped - a value with the passed <paramref name="sourceColumn"/>[<paramref name="index"/>]</returns>
55+
public ColumnMappingValue GetMapping(string sourceColumn, string index)
56+
{
57+
return GetMapping(CreateSourceName(sourceColumn, index));
58+
}
59+
60+
/// <summary>
61+
/// Gets a mapped property name
62+
/// </summary>
63+
/// <param name="sourceColumn">The name of the column to get a mapped property name for.</param>
64+
/// <returns>The mapped property name OR - if the name is unmapped - the passed <paramref name="sourceColumn"/>[<paramref name="index"/>]</returns>
65+
public string GetMappedProperty(string sourceColumn)
66+
{
67+
ColumnMappingValue result;
68+
69+
if (_map.TryGetValue(sourceColumn, out result))
70+
{
71+
return result.EntityProperty;
72+
}
73+
74+
return sourceColumn;
75+
}
76+
77+
/// <summary>
78+
/// Gets a mapped property name
79+
/// </summary>
80+
/// <param name="sourceColumn">The name of the column to get a mapped property name for.</param>
81+
/// <param name="index">The column index, e.g. a language code (de, en etc.)</param>
82+
/// <returns>The mapped property name OR - if the name is unmapped - the passed <paramref name="sourceColumn"/>[<paramref name="index"/>]</returns>
83+
public string GetMappedProperty(string sourceColumn, string index)
84+
{
85+
return GetMappedProperty(CreateSourceName(sourceColumn, index));
86+
}
87+
88+
internal static string CreateSourceName(string name, string index)
89+
{
90+
if (index.HasValue())
91+
{
92+
name += String.Concat("[", index, "]");
93+
}
94+
95+
return name;
96+
}
97+
}
98+
99+
100+
public class ColumnMappingValue
101+
{
102+
/// <summary>
103+
/// The property name of the target entity
104+
/// </summary>
105+
public string EntityProperty { get; set; }
106+
107+
/// <summary>
108+
/// An optional default value
109+
/// </summary>
110+
public string DefaultValue { get; set; }
111+
}
112+
}

src/Libraries/SmartStore.Services/DataExchange/ColumnMapConverter.cs renamed to src/Libraries/SmartStore.Services/DataExchange/Import/ColumnMapping/ColumnMapConverter.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using Newtonsoft.Json;
55
using SmartStore.ComponentModel;
66

7-
namespace SmartStore.Services.DataExchange
7+
namespace SmartStore.Services.DataExchange.Import
88
{
99
public class ColumnMapConverter : TypeConverterBase
1010
{
@@ -41,6 +41,14 @@ public override object ConvertFrom(CultureInfo culture, object value)
4141
return base.ConvertFrom(culture, value);
4242
}
4343

44+
public T ConvertFrom<T>(string value)
45+
{
46+
if (value.HasValue())
47+
return (T)ConvertFrom(CultureInfo.InvariantCulture, value);
48+
49+
return default(T);
50+
}
51+
4452
public override object ConvertTo(CultureInfo culture, string format, object value, Type to)
4553
{
4654
if (to == typeof(string))
@@ -57,5 +65,10 @@ public override object ConvertTo(CultureInfo culture, string format, object valu
5765

5866
return base.ConvertTo(culture, format, value, to);
5967
}
68+
69+
public string ConvertTo(object value)
70+
{
71+
return (string)ConvertTo(CultureInfo.InvariantCulture, null, value, typeof(string));
72+
}
6073
}
6174
}

src/Libraries/SmartStore.Services/SmartStore.Services.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@
174174
<Compile Include="Common\UAParserUserAgent.cs" />
175175
<Compile Include="Customers\CustomerRegisteredEvent.cs" />
176176
<Compile Include="Customers\Importer\CustomerImporter.cs" />
177-
<Compile Include="DataExchange\ColumnMap.cs" />
178-
<Compile Include="DataExchange\ColumnMapConverter.cs" />
177+
<Compile Include="DataExchange\Import\ColumnMapping\ColumnMap.cs" />
178+
<Compile Include="DataExchange\Import\ColumnMapping\ColumnMapConverter.cs" />
179179
<Compile Include="DataExchange\Csv\CsvConfigurationConverter.cs" />
180180
<Compile Include="DataExchange\Csv\CsvConfiguration.cs" />
181181
<Compile Include="DataExchange\Csv\CsvDataReader.cs" />

src/Presentation/SmartStore.Web/Administration/Controllers/ImportController.cs

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,68 @@ public ImportController(
4545

4646
#region Utilities
4747

48+
private List<ColumnMappingItemModel> PrepareColumnMappingModels(ImportProfile profile, CsvConfiguration csvConfiguration)
49+
{
50+
var models = new List<ColumnMappingItemModel>();
51+
52+
try
53+
{
54+
var mapConverter = new ColumnMapConverter();
55+
var map = mapConverter.ConvertFrom<ColumnMap>(profile.ColumnMapping);
56+
57+
if (map != null && map.Mappings.Any())
58+
{
59+
models = map.Mappings
60+
.Select(x =>
61+
{
62+
var mapModel = new ColumnMappingItemModel
63+
{
64+
SourceColumn = x.Key,
65+
EntityProperty = x.Value.EntityProperty,
66+
DefaultValue = x.Value.DefaultValue
67+
};
68+
return mapModel;
69+
})
70+
.ToList();
71+
}
72+
else
73+
{
74+
var files = profile.GetImportFiles();
75+
if (files.Any())
76+
{
77+
var filePath = files.First();
78+
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
79+
{
80+
var dataTable = LightweightDataTable.FromFile(Path.GetFileName(filePath), stream, stream.Length, csvConfiguration, 0, 2);
81+
82+
foreach (var column in dataTable.Columns)
83+
{
84+
models.Add(new ColumnMappingItemModel
85+
{
86+
SourceColumn = column.Name
87+
});
88+
}
89+
90+
return models;
91+
}
92+
}
93+
}
94+
}
95+
catch (Exception exception)
96+
{
97+
NotifyError(exception);
98+
}
99+
return models;
100+
}
101+
48102
private void PrepareProfileModel(ImportProfileModel model, ImportProfile profile, bool forEdit)
49103
{
104+
if (forEdit)
105+
{
106+
model.AvailableEntityTypes = ImportEntityType.Product.ToSelectList(false).ToList();
107+
model.AvailableFileTypes = ImportFileType.CSV.ToSelectList(false).ToList();
108+
}
109+
50110
if (profile != null)
51111
{
52112
model.Id = profile.Id;
@@ -67,25 +127,26 @@ private void PrepareProfileModel(ImportProfileModel model, ImportProfile profile
67127
.Select(x => Path.GetFileName(x))
68128
.ToList();
69129

70-
if (profile.FileType == ImportFileType.CSV)
130+
if (forEdit)
71131
{
72-
var converter = new CsvConfigurationConverter();
73-
var config = converter.ConvertFrom<CsvConfiguration>(profile.FileTypeConfiguration);
132+
CsvConfiguration csvConfiguration = null;
133+
134+
if (profile.FileType == ImportFileType.CSV)
135+
{
136+
var csvConverter = new CsvConfigurationConverter();
137+
csvConfiguration = csvConverter.ConvertFrom<CsvConfiguration>(profile.FileTypeConfiguration);
138+
139+
model.CsvConfiguration = new CsvConfigurationModel(csvConfiguration ?? CsvConfiguration.ExcelFriendlyConfiguration);
140+
}
74141

75-
model.CsvConfiguration = new CsvConfigurationModel(config ?? CsvConfiguration.ExcelFriendlyConfiguration);
142+
model.ColumnMappings = PrepareColumnMappingModels(profile, csvConfiguration);
76143
}
77144
}
78145
else
79146
{
80147
model.Name = model.EntityType.GetLocalizedEnum(_services.Localization, _services.WorkContext);
81148
model.ExistingFileNames = new List<string>();
82149
}
83-
84-
if (forEdit)
85-
{
86-
model.AvailableEntityTypes = ImportEntityType.Product.ToSelectList(false).ToList();
87-
model.AvailableFileTypes = ImportFileType.CSV.ToSelectList(false).ToList();
88-
}
89150
}
90151

91152
#endregion
@@ -192,12 +253,32 @@ public ActionResult Edit(ImportProfileModel model, bool continueEditing, FormCol
192253
profile.Skip = model.Skip;
193254
profile.Take = model.Take;
194255

195-
if (profile.FileType == ImportFileType.CSV && model.CsvConfiguration != null)
256+
if (model.ColumnMappings != null && model.ColumnMappings.Any())
257+
{
258+
var map = new ColumnMap();
259+
var mapConverter = new ColumnMapConverter();
260+
261+
model.ColumnMappings.Each(x => map.AddMapping(x.SourceColumn, x.EntityProperty, x.DefaultValue));
262+
profile.ColumnMapping = mapConverter.ConvertTo(map);
263+
}
264+
else
265+
{
266+
profile.ColumnMapping = null;
267+
}
268+
269+
if (profile.FileType == ImportFileType.CSV)
196270
{
197-
CsvConfiguration config = model.CsvConfiguration.Clone();
271+
if (model.CsvConfiguration != null)
272+
{
273+
CsvConfiguration config = model.CsvConfiguration.Clone();
198274

199-
var converter = new CsvConfigurationConverter();
200-
profile.FileTypeConfiguration = converter.ConvertTo(config);
275+
var csvConverter = new CsvConfigurationConverter();
276+
profile.FileTypeConfiguration = csvConverter.ConvertTo(config);
277+
}
278+
else
279+
{
280+
profile.FileTypeConfiguration = null;
281+
}
201282
}
202283

203284
_importService.UpdateImportProfile(profile);

0 commit comments

Comments
 (0)