Yesterday I finished up another Alchemy SDK class. One of the requests that came up was for a bulk loader utility. The customer had exported the data from some other file management tool as a series of files and an XML file describing it all and wanted to bring it in to Alchemy. So I tackled the problem on the last evening before the end of the class and ended up with a pretty good solution. But I didn't actually have a source file beyond what the customer described in class, so I built a tool to come up with bogus data as well.
First lets look at the XML Export File. Its pretty simple stuff.
<Documents>
<Document>
<DocumentName>ry7yGR9GDTPD</DocumentName>
<ExportedFilePath>c:\test\9.tif</ExportedFilePath>
<DatabasePath>Amsterdam</DatabasePath>
<FileDate>04/02/00</FileDate>
<LoanDate>08/02/03</LoanDate>
<LoanAmount>14811.90</LoanAmount>
</Document>
<Documents>
Each of the elements other than the file's location needed to be turned into fields in the Alchemy Database.
The Bulk Loader utility simply reads that in as an XMLDocument, creates the fields if necessary, then loops through all the Document elements adding the file and setting the fields. Adding 5000 single page tiffs took less than five minutes.
class BulkImport
{ static string XmlFilePath = "";
static string DatabasePath = "";
static Alchemy.Application auApp = new Alchemy.Application();
static Alchemy.Database auDB;
static void Main(string[] args)
{ if (GetParameters(args))
{ //auApp.LoadOptionsFile("");
try
{ auDB = auApp.Databases.Add(DatabasePath);
}
catch (System.Runtime.InteropServices.COMException ex)
{ Console.WriteLine("There was a problem adding the database." + ex.ToString()); return;
}
CreateFields();
XmlDocument ImportedFiles = new XmlDocument();
ImportedFiles.Load(XmlFilePath);
foreach (XmlNode documentNode in ImportedFiles.SelectNodes("/Documents/*")) AddDocumentToDatabase(documentNode);
}
}
/// <param name="documentNode">Node in the XML file describing the document to be added</param>
private static void AddDocumentToDatabase(XmlNode documentNode)
{ string documentName = documentNode["DocumentName"].InnerText;
string sourcePath = documentNode["ExportedFilePath"].InnerText;
string databasePath = documentNode["DatabasePath"].InnerText;
DateTime fileDate = Convert.ToDateTime(documentNode["FileDate"].InnerText);
DateTime loanDate = Convert.ToDateTime(documentNode["LoanDate"].InnerText);
string loanAmount = documentNode["LoanAmount"].InnerText;
Alchemy.Item newItem = auDB.Root;
Alchemy.Item Folder = auDB.GetItemByTitlePath(databasePath, auDB.Root) ?? CreateFolder(databasePath);
newItem = Folder.CreateItem(Alchemy.AuPosEnum.auPosLastChild, sourcePath);
newItem.set_Field("Document Title", documentName); newItem.set_Field("ExportedFilePath", sourcePath); newItem.set_Field("FileDate", fileDate); newItem.set_Field("LoanDate", loanDate); newItem.set_Field("LoanAmount", loanAmount); }
/// <param name="FolderName">Name of the folder where the item is being added</param>
/// <returns>The folder that is created</returns>
static Alchemy.Item CreateFolder(string FolderName)
{ Alchemy.Item Folder = auDB.Root.CreateFolder(Alchemy.AuPosEnum.auPosLastChild);
Folder.set_Field("Folder Title", FolderName); return Folder;
}
static bool GetParameters(string[] args)
{ bool returnValue = false;
if (args.Length==2)
{ XmlFilePath = args[0];
DatabasePath = args[1];
returnValue = true;
}
else
Console.WriteLine("GetParameters: The correct syntax for this tool is BulkImport XmlFilePath DatabasePath");
return returnValue;
}
/// <summary>
/// If the fields in the database do not already exist, create them.
/// </summary>
static void CreateFields()
{ string[] XmlFields = { "ExportedFilePath", "FileDate","LoanDate","LoanAmount" };
foreach (string fieldName in XmlFields)
{ bool fieldExists = false;
foreach (Alchemy.Field dbField in auDB.Fields)
{ if (fieldName == dbField.Name)
fieldExists = true;
}
if (!fieldExists)
{ Alchemy.AuDataTypeEnum dtEnum = Alchemy.AuDataTypeEnum.auDataText;
if (fieldName.ToLower().Contains("date")) dtEnum = Alchemy.AuDataTypeEnum.auDataDate;
auDB.Fields.Add(fieldName, dtEnum);
}
}
}
}
The only step I don't do is a build, but that would have been easy enough to add as well.
The Test XML Creation tool was also an easy little app, but potentially very useful for me in the future, so I am posting that here too. Whats interesting about this is the randomness. I come up with random dates, names, and folder names. Nothing too complex, but enough to keep it interesting.
class XMLCreate
{ static void Main(string[] args)
{ if (args.Length > 0)
{ int count = 0;
count = Convert.ToInt32(args[0]);
XmlDocument xDoc = new XmlDocument();
XmlDeclaration xDeclare = xDoc.CreateXmlDeclaration("1.0", "utf-8", null); XmlElement rootNode = xDoc.CreateElement("Documents"); xDoc.InsertBefore(xDeclare, xDoc.DocumentElement);
xDoc.AppendChild(rootNode);
Random rnd = new Random(DateTime.Now.Second);
for (int i = 0; i < count; i++)
{ XmlElement documentNode = CreateDocNode(xDoc);
CreateDocName(xDoc, documentNode, rnd);
CreateFilePath(xDoc, documentNode, rnd);
CreateDatabasePath(xDoc, documentNode, rnd);
CreateFileDate(xDoc,documentNode,rnd);
CreateLoanDate(xDoc, documentNode, rnd);
CreateLoanAmount(xDoc, documentNode, rnd);
}
xDoc.Save("c:\\testxml.xml");
}
}
private static void CreateNode(XmlDocument xDoc, XmlElement documentNode, string elementName, string elementText)
{ XmlElement NodeName = xDoc.CreateElement(elementName);
XmlText NodeText = xDoc.CreateTextNode(elementText);
documentNode.AppendChild(NodeName);
NodeName.AppendChild(NodeText);
}
private static void CreateLoanAmount(XmlDocument xDoc, XmlElement documentNode, Random rnd)
{ Double randomLoanAmount = rnd.NextDouble() * rnd.Next(1000, 100000);
CreateNode(xDoc, documentNode, "LoanAmount", randomLoanAmount.ToString("#.00")); }
private static void CreateLoanDate(XmlDocument xDoc, XmlElement documentNode, Random rnd)
{ DateTime randomDate = new DateTime(rnd.Next(1995, 2007), rnd.Next(1, 12), rnd.Next(1, 28));
CreateNode(xDoc, documentNode, "LoanDate", randomDate.ToString("MM/dd/yy")); }
private static void CreateFileDate(XmlDocument xDoc, XmlElement documentNode, Random rnd)
{ DateTime randomDate = new DateTime(rnd.Next(1995, 2007), rnd.Next(1, 12), rnd.Next(1, 28));
CreateNode(xDoc, documentNode, "FileDate", randomDate.ToString("MM/dd/yy")); }
private static void CreateDatabasePath(XmlDocument xDoc, XmlElement documentNode, Random rnd)
{ string[] cityNames = new string[] { "Seattle", "Amsterdam", "Dublin", "Cairo" }; string randomDatabasePath = cityNames[rnd.Next(1, cityNames.Length)];
CreateNode(xDoc, documentNode, "DatabasePath", randomDatabasePath);
}
private static void CreateFilePath(XmlDocument xDoc, XmlElement documentNode, Random rnd)
{ string randomFilePath = "c:\\test\\" + rnd.Next(1, 10).ToString() + ".tif";
CreateNode(xDoc, documentNode, "ExportedFilePath", randomFilePath);
}
private static XmlElement CreateDocNode(XmlDocument xDoc)
{ XmlElement docNode = xDoc.CreateElement("Document"); xDoc.DocumentElement.PrependChild(docNode);
return docNode;
}
private static void CreateDocName(XmlDocument xDoc, XmlElement documentNode, Random rnd)
{ char[] availChars = "abcdefgijkmnopqrstwxyzABCDEFGHJKLMNPQRSTWXYZ123456789".ToCharArray();
string randomWord = "";
char ch;
for (int i = 0; i < rnd.Next(10, 15); i++)
{ int randomCharPosition = rnd.Next(1, availChars.Length);
randomWord += availChars[randomCharPosition];
}
CreateNode(xDoc, documentNode, "DocumentName", randomWord);
}
}