Fixed-width text files in data management (F&O)

Data management in Dynamics 365 for Finance and Operations supports several types of files for import and export. I usually see XML, Excel and CSV used, but sometimes we also need to deal with a text format with fixed-width columns. It means that there is no column separator as in CSV – we know that first ten characters contains an ID, next eight characters contains a quantity or something like that.

Data management supports this scenario too, which not everyone is aware of.

To try it, first set up a source format. Go to Data management workspace, click Configure data source tile and create a new format. Set File format = Fixed width and possibly other parameters as needed. I’ll use First row header = No and Row delimiter = {LF}. It doesn’t seem to work very well with {CR}{LF}.

Then create an export project and add an entity to be exported with our new source format. I used Customer groups entity (because it’s relatively simple and it doesn’t contain too many records).

Still in the project, notice Entity attribute button. It’s very important when talking about fixed-width files.

If you press it, you’ll see a list of columns that will be in the file. There is a sequence of columns, their length etc., and you can modify it as needed.

If you want to import the file, create an import project and add an entity with the fixed-width source format. It’ll complain about missing mapping, unless the columns have headers and data management is able to map them to fields.

In this case, we’ll configure it manually. As we already know, the Entity attribute button will take us into the setup of file columns. Here we can add the columns, their type, lenght and so on.

Then we can use these columns in source mapping in the usual way.

By the way, when I needed to deal with fixed-width text files in C# (namely Azure Functions), I used FixedWidthParserWriter, which did the job perfectly.

Animated GIF thumbnails from a video

This blog post isn’t about Dynamics AX / Dynamics 365, although it may occasionally be useful for some AX/D365 developers as well.

Let’s say I have a 30 minutes long movie that I want to share on a website and I want to give people an idea about the content of the video. Therefore I want to create a slideshow of screenshots taken from the movie at regular intervals. An animated GIF is an obvious choice.

I spent quite some time trying to figure out how to do it, because I had little idea. After some research, I came to a conclusion that a reasonable approach is extracting screenshots with FFmpeg and then combining them to a GIF file with ImageMagick.

Because installing those console applications and dealing with them directly would be quite cumbersome, I also looked for some wrapper libraries available via NuGet. I chose these:

Now let’s build a simple application to show the code. Create a console application for .NET Framework and add those two NuGet packages.

Add using statements for namespaces that we’ll need in a moment:

using System.IO;
using ImageMagick;
using MediaToolkit;
using MediaToolkit.Model;
using MediaToolkit.Options;

The structure of your program will be following:

class Program
{
    static void Main(string[] args)
    {
        new Program().Run();
    }
 
    void Run()
    {
        string videoFilePath = @"c:\temp\input.mp4";
 
        List<string> thumbnails = ExtractThumbnails(videoFilePath, 10);
 
        if (thumbnails?.Any() == true)
        {
            string gifFilePath = Path.Combine(
                Path.GetDirectoryName(videoFilePath),
                $"{Path.GetFileName(videoFilePath)}.gif");
 
            CombineThumbnails(gifFilePath, thumbnails);
 
            // Delete temporary files
            thumbnails.ForEach(file => File.Delete(file));
        }
    }
}

ExtractThumbnails() takes screenshots from the video as JPG files.

CombineThumbnails() then take these files and put them into an animated GIF.

Finally we delete JPG files, because they aren’t needed anymore.

Here is the implementation of ExtractThumbnails(). The key part is the call of engine.GetThumbnail() at the end.

List<string> ExtractThumbnails(string inputFilePath, int numOfPics)
{
    MediaFile inputFile = new MediaFile { Filename = inputFilePath };
    List<string> thumbnails = new List<string>(numOfPics);
 
    using (var engine = new Engine())
    {
        engine.GetMetadata(inputFile);
        if (inputFile.Metadata == null)
        {
            throw new InvalidOperationException("Invalid file");
        }
 
        int duration = (int)inputFile.Metadata.Duration.TotalSeconds;
        int picDistance = duration / (numOfPics + 1);
 
        for (int i = 1; i <= numOfPics; i++)
        {
            var options = new ConversionOptions { Seek = TimeSpan.FromSeconds(i * picDistance) };
 
            string outputFilePath = $"{inputFilePath}{i}.jpg";
 
            var outputFile = new MediaFile { Filename = outputFilePath };
            engine.GetThumbnail(inputFile, outputFile, options);
            thumbnails.Add(outputFilePath);
        }
    }
 
    return thumbnails;
}

The last missing piece is the method creating the animated GIF. It’s straightforward, thanks to Magick.NET:

void CombineThumbnails(string gifFilePath, List<string> thumbnails)
{
    using (var collection = new MagickImageCollection())
    {
        for (int i = 0; i < thumbnails.Count; i++)
        {
            collection.Add(thumbnails[i]);
            collection[i].AnimationDelay = 70;
            collection[i].Resize(800, 600);
        }
 
        collection.Write(gifFilePath);
    }
}