Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VCST-2487: Enhance CSV module with new features and refactorings #111

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 22 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,74 +1,36 @@
# Catalog CSV Import module
# CSV Catalog Export and Import Module

[![CI status](https://github.com/VirtoCommerce/vc-module-catalog-csv-import/workflows/Module%20CI/badge.svg?branch=dev)](https://github.com/VirtoCommerce/vc-module-catalog-csv-import/actions?query=workflow%3A"Module+CI") [![Quality gate](https://sonarcloud.io/api/project_badges/measure?project=VirtoCommerce_vc-module-catalog-csv-import&metric=alert_status&branch=dev)](https://sonarcloud.io/dashboard?id=VirtoCommerce_vc-module-catalog-csv-import) [![Reliability rating](https://sonarcloud.io/api/project_badges/measure?project=VirtoCommerce_vc-module-catalog-csv-import&metric=reliability_rating&branch=dev)](https://sonarcloud.io/dashboard?id=VirtoCommerce_vc-module-catalog-csv-import) [![Security rating](https://sonarcloud.io/api/project_badges/measure?project=VirtoCommerce_vc-module-catalog-csv-import&metric=security_rating&branch=dev)](https://sonarcloud.io/dashboard?id=VirtoCommerce_vc-module-catalog-csv-import) [![Sqale rating](https://sonarcloud.io/api/project_badges/measure?project=VirtoCommerce_vc-module-catalog-csv-import&metric=sqale_rating&branch=dev)](https://sonarcloud.io/dashboard?id=VirtoCommerce_vc-module-catalog-csv-import)

Catalog CSV Import module
CSV Catalog Export and Import Module is a module for Virto Commerce platform that allows to export and import catalog data in CSV format.

# Installation
Installing the module:
* Automatically: in VC Manager go to Configuration -> Modules -> Catalog CSV Import module -> Install
* Manually: download module zip package from https://github.com/VirtoCommerce/vc-module-catalog-csv-import/releases. In VC Manager go to Configuration -> Modules -> Advanced -> upload module package -> Install.
## Overview
The CSV Catalog Export and Import Module Enables seamless export and import of catalog data in CSV format, simplifying data management and integration with external systems.

# Settings
## Features
1. Export catalog data to CSV format.
1. Import catalog data from CSV format.
1. Export and Import dynamic properties.
1. Export and Import multiple product images: urls and groups.
1. Configurable property mapping for import.
1. Can be configured to create a new dictionary items automatically during import.

Before starting using the Import functionality, the admin should set the 'Create new dictionary values' for Import Module. In order to set this parameter, the user should go through the following steps:

1. Open Settings→select CSVCatalogImport→select General;
1. The system will open the 'Settings values' blade;
1. The user switches on  the 'Create new dictionary value' switch and saves the changes;  
1. The system will save the changes and enable creation of new Property Dictionary Values during data import.
## References
* Home: https://virtocommerce.com
* Documantation: https://docs.virtocommerce.org
* Community: https://www.virtocommerce.org
* [Download Latest Release](https://github.com/VirtoCommerce/vc-module-catalog-csv-import/releases)

![Fig. Settings](docs/media/screen-settings.png)
![Fig. Setting values](docs/media/screen-setting-values.png)

## Catalog Import

### Scenario

1. The user opens Catalog module→ selects a Catalog and clicks 'Import';
![Catalog Import](docs/media/screen-select-catalog-to-import.png)
1. The system will open the Import types blade and allows the user to select CSV import format;
1. The user opens the next blade.
The system will prompt the user to do the following:

1. Select delimeter from drop down;
1. Upload a CSV file;
1. Map columns;

1. The user selects the delimeter, uploads the file and selects 'Map columns;
![Fig. Upload file](docs/media/screen-upload-file.png)
1. The system will display the next blade - Columns mapping;
1. The system automatically maps the CSV fields to to the same product properties;
![Mapping](docs/media/screen-mapping.png)
1. The user confirms the mapping results by clicking the 'OK' button;
1. The system will display the 'Import catalog from csv' blade and the 'Start Import' button will become active;
1. The user starts the import by clicking the 'Start Import' button;
1. The import report will be generated by the system once the import is completed;
1. The Import report should display the following information:

![Import Report](docs/media/screen-catalog-import.png)

## Model Mappings
### Seo Info
|CSV Property Name |Model Property Name|
|------------------|-------------------|
|SeoUrl|SemanticUrl|
|SeoTitle|PageTitle|
|SeoLanguage|LanguageCode|
|SeoDescription|MetaDescription|
|SeoMetaKeywords|MetaKeywords|
|SeoImageAlternativeText|ImageAltDescription|

# License
## License
Copyright (c) Virto Solutions LTD. All rights reserved.

Licensed under the Virto Commerce Open Software License (the "License"); you
This software is licensed under the Virto Commerce Open Software License (the "License"); you
may not use this file except in compliance with the License. You may
obtain a copy of the License at
obtain a copy of the License at http://virtocommerce.com/opensourcelicense.

http://virtocommerce.com/opensourcelicense

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
Unless required by the applicable law or agreed to in written form, the software
distributed under the License is provided on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.

134 changes: 102 additions & 32 deletions src/VirtoCommerce.CatalogCsvImportModule.Core/Model/CsvProduct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
using System.Globalization;
using System.Linq;
using Omu.ValueInjecter;
using VirtoCommerce.AssetsModule.Core.Assets;
using VirtoCommerce.CatalogModule.Core.Model;
using VirtoCommerce.CoreModule.Core.Seo;
using VirtoCommerce.InventoryModule.Core.Model;
using VirtoCommerce.AssetsModule.Core.Assets;
using VirtoCommerce.Platform.Core.Common;
using VirtoCommerce.PricingModule.Core.Model;

Expand Down Expand Up @@ -38,6 +38,7 @@ public CsvProduct(CatalogProduct product, IBlobUrlResolver blobUrlResolver, Pric
_blobUrlResolver = blobUrlResolver;

this.InjectFrom(product);

Properties = product.Properties;
Images = product.Images;
Assets = product.Assets;
Expand Down Expand Up @@ -162,71 +163,102 @@ public string Quantity
}
}

private string _primaryImage;
public string PrimaryImage
{
get
{
var retVal = string.Empty;
if (Images != null)
if (Images != null && _primaryImage == null)
{
var primaryImage = Images.OrderBy(x => x.SortOrder).FirstOrDefault();
if (primaryImage != null)
{
retVal = _blobUrlResolver != null ? _blobUrlResolver.GetAbsoluteUrl(primaryImage.Url) : primaryImage.Url;
_primaryImage = _blobUrlResolver != null ?
_blobUrlResolver.GetAbsoluteUrl(primaryImage.Url) :
primaryImage.Url;
OlegoO marked this conversation as resolved.
Show resolved Hide resolved
}
}
return retVal;
return _primaryImage;
}

set
{
if (!string.IsNullOrEmpty(value))
_primaryImage = value;
}
}

private string _primaryImageGroup;
public string PrimaryImageGroup
{
get
{
if (Images != null && _primaryImageGroup == null)
{
Images.Add(new Image
var primaryImage = Images.OrderBy(x => x.SortOrder).FirstOrDefault();
if (primaryImage != null)
{
Url = value,
SortOrder = 0,
Group = "images",
Name = value.Split('/').Last()
});
_primaryImageGroup = primaryImage.Group;
}
}
return _primaryImageGroup;
}
set
{
_primaryImageGroup = value;
}
}

private string _altImage;
public string AltImage
{
get
{
var retVal = string.Empty;
if (Images != null)
if (Images != null && _altImage == null)
{
var primaryImage = Images.OrderBy(x => x.SortOrder).Skip(1).FirstOrDefault();
if (primaryImage != null)
{
retVal = _blobUrlResolver != null ? _blobUrlResolver.GetAbsoluteUrl(primaryImage.Url) : primaryImage.Url;
}
var altImageUrls = Images
.Where(x => x.SortOrder > 0)
.OrderBy(x => x.SortOrder)
.Select(x => _blobUrlResolver != null ? _blobUrlResolver.GetAbsoluteUrl(x.Url) : x.Url)
.ToArray();

_altImage = string.Join(_csvCellDelimiter[1], altImageUrls);

}
return retVal;
return _altImage;
}

set
{
if (!string.IsNullOrEmpty(value))
_altImage = value;
}
}

private string _altImageGroup;

public string AltImageGroup
{
get
{
if (Images != null && _altImageGroup == null)
{
var altImages = value.Split(_csvCellDelimiter, StringSplitOptions.RemoveEmptyEntries);
foreach (string url in altImages)
{
Images.Add(new Image
{
Url = url,
SortOrder = 1,
Group = "images",
Name = url.Split('/').Last()
});
}
var altImageGroups = Images
.Where(x => x.SortOrder > 0)
.OrderBy(x => x.SortOrder)
.Select(x => x.Group)
.ToArray();

_altImageGroup = string.Join(_csvCellDelimiter[1], altImageGroups);

}
return _altImageGroup;
}

set
{
_altImageGroup = value;
}
}

public string Sku
{
get
Expand All @@ -246,7 +278,9 @@ public string CategoryPath
get
{
if (Category == null)
{
return null;
}

return Category.Path;
}
Expand Down Expand Up @@ -467,5 +501,41 @@ public void MergeFrom(CatalogProduct product)
}
SeoInfos = SeoInfos.Where(x => !x.SemanticUrl.IsNullOrEmpty()).Concat(product.SeoInfos).ToList();
}


public void CreateImagesFromFlatData()
{
var imageUrls = new List<string>();
var imageGropus = new List<string>();

if (!string.IsNullOrEmpty(PrimaryImage))
{
imageUrls.Add(PrimaryImage);
imageGropus.Add(PrimaryImageGroup);
}

if (!string.IsNullOrEmpty(AltImage))
{
imageUrls.AddRange(AltImage.Split(_csvCellDelimiter, StringSplitOptions.RemoveEmptyEntries));
imageGropus.AddRange(AltImageGroup.Split(_csvCellDelimiter, StringSplitOptions.RemoveEmptyEntries));
}

// Fill imageGropus with empty strings if its length is less than imageUrls
while (imageGropus.Count < imageUrls.Count)
{
imageGropus.Add(string.Empty);
}

var index = 0;
var images = imageUrls.Zip(imageGropus, (url, group) => new Image
{
Url = url,
Group = string.IsNullOrEmpty(group) ? "images" : group,
SortOrder = index++,
Name = UrlHelper.ExtractFileNameFromUrl(url)
});

this.Images.AddRange(images);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ public static CsvProductMappingConfiguration GetDefaultConfiguration()
var retVal = new CsvProductMappingConfiguration { Delimiter = ";" };

var requiredFields = new List<string>();
var optionalFields = ReflectionUtility.GetPropertyNames<CsvProduct>(x => x.Name, x => x.Id, x => x.Sku, x => x.CategoryPath, x => x.CategoryId, x => x.MainProductId, x => x.PrimaryImage, x => x.AltImage, x => x.SeoUrl, x => x.SeoTitle,
x => x.SeoDescription, x => x.SeoLanguage, x => x.SeoStore, x => x.SeoMetaKeywords, x => x.SeoImageAlternativeText, x => x.Review, x => x.ReviewType, x => x.IsActive, x => x.IsBuyable, x => x.TrackInventory,
var optionalFields = ReflectionUtility.GetPropertyNames<CsvProduct>(x => x.Name, x => x.Id, x => x.Sku, x => x.CategoryPath, x => x.CategoryId, x => x.MainProductId,
x => x.PrimaryImage, x => x.PrimaryImageGroup, x => x.AltImage, x => x.AltImageGroup,
x => x.SeoUrl, x => x.SeoTitle, x => x.SeoDescription, x => x.SeoLanguage, x => x.SeoStore, x => x.SeoMetaKeywords, x => x.SeoImageAlternativeText,
x => x.Review, x => x.ReviewType, x => x.IsActive, x => x.IsBuyable, x => x.TrackInventory,
x => x.PriceId, x => x.SalePrice, x => x.ListPrice, x => x.PriceMinQuantity, x => x.Currency, x => x.PriceListId, x => x.Quantity,
x => x.FulfillmentCenterId, x => x.PackageType, x => x.OuterId, x => x.Priority, x => x.MaxQuantity, x => x.MinQuantity,
x => x.ManufacturerPartNumber, x => x.Gtin, x => x.MeasureUnit, x => x.WeightUnit, x => x.Weight,
Expand Down
26 changes: 26 additions & 0 deletions src/VirtoCommerce.CatalogCsvImportModule.Core/UrlHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;

namespace VirtoCommerce.CatalogCsvImportModule.Core;
public static class UrlHelper
{
public static string ExtractFileNameFromUrl(string url)
{
ArgumentNullException.ThrowIfNullOrWhiteSpace(url);

if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out var uri))
{
if (!uri.IsAbsoluteUri)
{
uri = new Uri(new Uri("https://dummy-base/"), url);
}

// Get the file name from the path
var localPath = uri.LocalPath;
return localPath.Substring(localPath.LastIndexOf('/') + 1);
}
else
{
throw new UriFormatException($"Invalid URL format {url}.");
}
}
}
Loading
Loading