Skip to content

Commit 076217c

Browse files
committed
Finalized async excel import: localization, report generation, fixes, cleanup
1 parent 6b0f3bd commit 076217c

12 files changed

Lines changed: 427 additions & 47 deletions

File tree

src/Libraries/SmartStore.Core/Data/Impex/ImportResult.cs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,35 @@ public ImportMessage AddError(string message, ImportRowInfo affectedRow = null,
7171
return this.AddMessage(message, ImportMessageType.Error, affectedRow, affectedField);
7272
}
7373

74-
public ImportMessage AddError(Exception exception)
74+
public ImportMessage AddError(Exception exception, int? affectedBatch = null, string stage = null)
7575
{
76-
var message = exception.Message;
77-
if (exception.InnerException != null)
76+
var ex = exception;
77+
while (true)
7878
{
79-
message += " ({0})".FormatCurrent(exception.InnerException.Message);
79+
if (ex.InnerException == null)
80+
break;
81+
ex = ex.InnerException;
8082
}
81-
return this.AddMessage(message, ImportMessageType.Error);
83+
84+
var prefix = new List<string>();
85+
if (affectedBatch.HasValue)
86+
{
87+
prefix.Add("Batch: " + affectedBatch.Value);
88+
}
89+
if (stage.HasValue())
90+
{
91+
prefix.Add("Stage: " + stage);
92+
}
93+
94+
string msg = string.Empty;
95+
if (prefix.Any())
96+
{
97+
msg = "[{0}] ".FormatCurrent(String.Join(", ", prefix));
98+
}
99+
100+
msg += ex.Message;
101+
102+
return this.AddMessage(msg, ImportMessageType.Error);
82103
}
83104

84105
public ImportMessage AddMessage(string message, ImportMessageType severity, ImportRowInfo affectedRow = null, string affectedField = null)

src/Libraries/SmartStore.Core/Extensions/DateTimeExtensions.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,15 +453,13 @@ public static DateTime GetLastDayOfMonth(this DateTime date)
453453
return dtTo;
454454
}
455455

456-
/// <remarks>codehint: sm-add</remarks>
457456
public static DateTime ToEndOfTheDay(this DateTime dt)
458457
{
459458
if (dt != null)
460459
return new DateTime(dt.Year, dt.Month, dt.Day, 23, 59, 59);
461460
return dt;
462461
}
463462

464-
/// <remarks>codehint: sm-add</remarks>
465463
public static DateTime? ToEndOfTheDay(this DateTime? dt)
466464
{
467465
return (dt.HasValue ? dt.Value.ToEndOfTheDay() : dt);

src/Libraries/SmartStore.Data/Migrations/201403262015448_ImportExcel.Designer.cs

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
namespace SmartStore.Data.Migrations
2+
{
3+
using System;
4+
using System.Data.Entity.Migrations;
5+
using SmartStore.Data.Setup;
6+
7+
public partial class ImportExcel : DbMigration, ILocaleResourcesProvider, IDataSeeder<SmartObjectContext>
8+
{
9+
public override void Up()
10+
{
11+
}
12+
13+
public override void Down()
14+
{
15+
}
16+
17+
public bool RollbackOnFailure
18+
{
19+
get { return false; }
20+
}
21+
22+
public void Seed(SmartObjectContext context)
23+
{
24+
context.MigrateLocaleResources(MigrateLocaleResources);
25+
}
26+
27+
public void MigrateLocaleResources(LocaleResourcesBuilder builder)
28+
{
29+
30+
builder.AddOrUpdate("Common.Cancelled",
31+
"Cancelled",
32+
"Abgebrochen");
33+
34+
builder.AddOrUpdate("Admin.Common.ImportFromExcel.InProgress",
35+
"The import is being performed in the background now. You can view the progress or the result of the last completed import in the import dialog at any time.",
36+
"Der Import läuft jetzt im Hintergrund . Sie können den Fortschritt bzw. das Ergebnis des letzten Importvorganges jederzeit im Import-Dialog einsehen.");
37+
38+
builder.AddOrUpdate("Admin.Common.ImportFromExcel.LastResultTitle",
39+
"<b>Last import</b>: {0}{1}.",
40+
"<b>Letzter Import</b>: {0}{1}.");
41+
42+
builder.AddOrUpdate("Admin.Common.ImportFromExcel.ProcessedCount",
43+
"{0} of {1} rows processed.",
44+
"{0} von {1} Zeilen verarbeitet.");
45+
46+
builder.AddOrUpdate("Admin.Common.ImportFromExcel.QuickStats",
47+
"{0} new, {1} updated - with {2} warning(s) und {3} error(s).",
48+
"{0} neu, {1} aktualisiert - bei {2} Warnung(en) und {3} Fehler(n).");
49+
50+
builder.AddOrUpdate("Admin.Common.ImportFromExcel.ActiveSince",
51+
"Active since: {0}.",
52+
"Aktiv seit: {0}.");
53+
54+
builder.AddOrUpdate("Admin.Common.ImportFromExcel.CancelPrompt",
55+
"Do you really want to cancel the import? Products imported so far will not be removed.",
56+
"Soll der aktive Importvorgang wirklich abgebrochen werden? Bislang importierte Produkte werden nicht gelöscht.");
57+
58+
builder.AddOrUpdate("Admin.Common.ImportFromExcel.Cancel",
59+
"Cancel import",
60+
"Import abbrechen");
61+
62+
builder.AddOrUpdate("Admin.Common.ImportFromExcel.Cancelled",
63+
"Import process has been cancelled",
64+
"Import wurde abgebrochen");
65+
66+
builder.AddOrUpdate("Admin.Common.ImportFromExcel.DownloadReport",
67+
"Download full report...",
68+
"Vollständigen Bericht runterladen...");
69+
70+
builder.AddOrUpdate("Admin.Common.ImportFromExcel.NoReportAvailable",
71+
"No report available",
72+
"Kein Bericht verfügbar");
73+
}
74+
}
75+
}

src/Libraries/SmartStore.Data/Migrations/201403262015448_ImportExcel.resx

Lines changed: 126 additions & 0 deletions
Large diffs are not rendered by default.

src/Libraries/SmartStore.Data/SmartStore.Data.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@
119119
<Compile Include="Migrations\201403251610279_AboutPage.Designer.cs">
120120
<DependentUpon>201403251610279_AboutPage.cs</DependentUpon>
121121
</Compile>
122+
<Compile Include="Migrations\201403262015448_ImportExcel.cs" />
123+
<Compile Include="Migrations\201403262015448_ImportExcel.Designer.cs">
124+
<DependentUpon>201403262015448_ImportExcel.cs</DependentUpon>
125+
</Compile>
122126
<Compile Include="Setup\Builder\SettingsBuilder.cs" />
123127
<Compile Include="Setup\Builder\SettingsMigrator.cs" />
124128
<Compile Include="Setup\DbMigrationContext.cs" />
@@ -271,6 +275,9 @@
271275
<EmbeddedResource Include="Migrations\201403251610279_AboutPage.resx">
272276
<DependentUpon>201403251610279_AboutPage.cs</DependentUpon>
273277
</EmbeddedResource>
278+
<EmbeddedResource Include="Migrations\201403262015448_ImportExcel.resx">
279+
<DependentUpon>201403262015448_ImportExcel.cs</DependentUpon>
280+
</EmbeddedResource>
274281
<EmbeddedResource Include="Sql\Indexes.sql" />
275282
<EmbeddedResource Include="Sql\StoredProcedures.sql" />
276283
</ItemGroup>

src/Libraries/SmartStore.Services/ExportImport/IImportManager.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ Task<ImportResult> ImportProductsFromExcelAsync(
1515
Stream stream,
1616
CancellationToken cancellationToken,
1717
IProgress<ImportProgressInfo> progress = null);
18+
19+
/// <summary>
20+
/// Dumps an <see cref="ImportResult"/> instance to a string
21+
/// </summary>
22+
/// <param name="result">The result instance</param>
23+
/// <returns>The report</returns>
24+
string CreateTextReport(ImportResult result);
1825
}
1926

2027
public static class IImportManagerExtensions

src/Libraries/SmartStore.Services/ExportImport/ImportManager.cs

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using SmartStore.Services.Media;
1313
using SmartStore.Services.Seo;
1414
using SmartStore.Utilities;
15+
using System.Text;
1516

1617
namespace SmartStore.Services.ExportImport
1718
{
@@ -55,6 +56,60 @@ public ImportManager(
5556
this._rsProductPicture = rsProductPicture;
5657
}
5758

59+
public virtual string CreateTextReport(ImportResult result)
60+
{
61+
var sb = new StringBuilder();
62+
63+
using (var writer = new StringWriter(sb))
64+
{
65+
writer.WriteLine("SUMMARY");
66+
writer.WriteLine("==================================================================================");
67+
writer.WriteLine("Started: {0}".FormatCurrent(result.StartDateUtc.ToLocalTime()));
68+
writer.WriteLine("Finished: {0}{1}".FormatCurrent(result.EndDateUtc.ToLocalTime(), result.Cancelled ? " (cancelled by user)" : ""));
69+
writer.WriteLine("Duration: {0}".FormatCurrent((result.EndDateUtc - result.StartDateUtc).ToString("g")));
70+
71+
writer.WriteLine("");
72+
writer.WriteLine("Total rows in source: {0}".FormatCurrent(result.TotalRecords));
73+
writer.WriteLine("Rows processed: {0}".FormatCurrent(result.AffectedRecords));
74+
writer.WriteLine("Products imported: {0}".FormatCurrent(result.NewRecords));
75+
writer.WriteLine("Products updated: {0}".FormatCurrent(result.ModifiedRecords));
76+
77+
writer.WriteLine("");
78+
writer.WriteLine("Warnings: {0}".FormatCurrent(result.Messages.Count(x => x.MessageType == ImportMessageType.Warning)));
79+
writer.WriteLine("Errors: {0}".FormatCurrent(result.Messages.Count(x => x.MessageType == ImportMessageType.Error)));
80+
81+
writer.WriteLine("");
82+
writer.WriteLine("");
83+
writer.WriteLine("MESSAGES");
84+
writer.WriteLine("==================================================================================");
85+
86+
foreach (var message in result.Messages)
87+
{
88+
string msg = string.Empty;
89+
var prefix = new List<string>();
90+
if (message.AffectedItem != null)
91+
{
92+
prefix.Add("Pos: " + message.AffectedItem.Position + 1);
93+
}
94+
if (message.AffectedField.HasValue())
95+
{
96+
prefix.Add("Field: " + message.AffectedField);
97+
}
98+
99+
if (prefix.Any())
100+
{
101+
msg = "[{0}] ".FormatCurrent(String.Join(", ", prefix));
102+
}
103+
104+
msg += message.Message;
105+
106+
writer.WriteLine("{0}: {1}".FormatCurrent(message.MessageType.ToString().ToUpper(), msg));
107+
}
108+
}
109+
110+
return sb.ToString();
111+
}
112+
58113
/// <summary>
59114
/// Import products from XLSX file
60115
/// </summary>
@@ -69,6 +124,7 @@ public virtual async Task<ImportResult> ImportProductsFromExcelAsync(
69124
var t = await Task.Run<ImportResult>(async () => {
70125

71126
var result = new ImportResult();
127+
int saved = 0;
72128

73129
using (var scope = new DbContextScope(ctx: _rsProduct.Context, autoDetectChanges: false, proxyCreation: false, validateOnSave: false))
74130
{
@@ -100,11 +156,11 @@ public virtual async Task<ImportResult> ImportProductsFromExcelAsync(
100156
// ===========================================================================
101157
try
102158
{
103-
await ProcessProducts(batch, result);
159+
saved = await ProcessProducts(batch, result);
104160
}
105161
catch (Exception ex)
106162
{
107-
result.AddError(ex);
163+
result.AddError(ex, segmenter.CurrentSegment, "ProcessProducts");
108164
}
109165

110166
// reduce batch to saved (valid) products.
@@ -122,7 +178,9 @@ public virtual async Task<ImportResult> ImportProductsFromExcelAsync(
122178
// ===========================================================================
123179
if (batch.Any(x => x.IsNew || (x.ContainsKey("SeName") || x.NameChanged)))
124180
{
181+
_rsProduct.Context.AutoDetectChangesEnabled = true;
125182
ProcessSlugs(batch, result);
183+
_rsProduct.Context.AutoDetectChangesEnabled = false;
126184
}
127185

128186
// ===========================================================================
@@ -136,7 +194,7 @@ public virtual async Task<ImportResult> ImportProductsFromExcelAsync(
136194
}
137195
catch (Exception ex)
138196
{
139-
result.AddError(ex);
197+
result.AddError(ex, segmenter.CurrentSegment, "ProcessProductCategories");
140198
}
141199
}
142200

@@ -151,7 +209,7 @@ public virtual async Task<ImportResult> ImportProductsFromExcelAsync(
151209
}
152210
catch (Exception ex)
153211
{
154-
result.AddError(ex);
212+
result.AddError(ex, segmenter.CurrentSegment, "ProcessProductManufacturers");
155213
}
156214
}
157215

@@ -166,7 +224,7 @@ public virtual async Task<ImportResult> ImportProductsFromExcelAsync(
166224
}
167225
catch (Exception ex)
168226
{
169-
result.AddError(ex);
227+
result.AddError(ex, segmenter.CurrentSegment, "ProcessProductPictures");
170228
}
171229
}
172230

@@ -268,7 +326,7 @@ private async Task<int> ProcessProducts(ICollection<ImportRow<Product>> batch, I
268326
row.SetProperty(result, product, (x) => x.MaxNumberOfDownloads, 10);
269327
row.SetProperty(result, product, (x) => x.DownloadActivationTypeId, 1);
270328
row.SetProperty(result, product, (x) => x.HasSampleDownload);
271-
row.SetProperty(result, product, (x) => x.SampleDownloadId);
329+
row.SetProperty(result, product, (x) => x.SampleDownloadId, (int?)null, ZeroToNull);
272330
row.SetProperty(result, product, (x) => x.HasUserAgreement);
273331
row.SetProperty(result, product, (x) => x.UserAgreementText);
274332
row.SetProperty(result, product, (x) => x.IsRecurring);
@@ -545,6 +603,18 @@ private void ProcessProductPictures(ICollection<ImportRow<Product>> batch, Impor
545603
return null;
546604
}
547605

606+
607+
private int? ZeroToNull(object value)
608+
{
609+
int result;
610+
if (CommonHelper.TryConvert<int>(value, out result) && result > 0)
611+
{
612+
return result;
613+
}
614+
615+
return (int?)null;
616+
}
617+
548618
/// <summary>
549619
/// Finds an equal picture by comparing the binary buffer
550620
/// </summary>

src/Presentation/SmartStore.Web/Administration/Content/variables.custom.less

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
// -------------------------
1515
@pageTitleFontFamily: 'Segoe UI Semibold', 'Segoe UI', Tahoma, 'Helvetica Neue', Helvetica, Arial, 'sans-serif';
1616
@pageTitleFontWeight: 600;
17-
@pageTitleColor: #818181;
17+
@pageTitleColor: #777;
1818

0 commit comments

Comments
 (0)