Last active
June 6, 2025 10:59
-
-
Save smileham/1e57a5946235e780dee5a824f664aa3d to your computer and use it in GitHub Desktop.
Import from CSV #jarchi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Import from CSV | |
* | |
* Requires jArchi - https://www.archimatetool.com/blog/2018/07/02/jarchi/ | |
* Requires PapaParse - https://www.papaparse.com/ | |
* Works with Export to CSV Script - https://gist.github.com/smileham/15c445b17a92bd6f5dc1508e573bcd8a | |
* | |
* Version 1: Import from CSV | |
* Version 1.1: Force character encoding to use UTF-8 | |
* Version 2: Support for Specialization and creates "CSVImport-timestamp" view | |
* Version 2.1: Fix for "empty" rows | |
* Version 2.2: Fix for BOM | |
* Version 2.3: Enhanced error detection | |
* | |
* (c) 2025 Steven Mileham | |
* | |
*/ | |
function parseCSV(filePath) { | |
const Papa = require(__DIR__ + "lib/papaparse.min.js"); | |
console.log("> Loaded Papa Parse"); | |
let FileReader = Java.type("java.io.FileReader"); | |
const Types = Java.type("java.nio.charset.StandardCharsets"); | |
let theCSVFile = new FileReader(filePath,Types.UTF_8); | |
let theCSV =""; | |
let data = theCSVFile.read(); | |
console.log("> Please Wait..."); | |
while(data != -1) { | |
if (data!=65279) { | |
let theCharacter = String.fromCharCode(data); | |
theCSV+=theCharacter; | |
} | |
data = theCSVFile.read(); | |
} | |
theCSVFile.close(); | |
console.log("> File Loaded"); | |
return Papa.parse(theCSV); | |
} | |
var debug = false; | |
console.show(); | |
console.clear(); | |
console.log("> Import CSV"); | |
const filePath = window.promptOpenFile({ title: "Open CSV", filterExtensions: ["*.CSV"], fileName: "default.csv" }); | |
if (filePath) { | |
theDataFile = parseCSV(filePath); | |
theData = theDataFile.data; | |
if (theDataFile.errors.length > 0) { | |
let errorMsg = "Errors found in CSV file:\n"; | |
theDataFile.errors.forEach(function(error) { | |
errorMsg += `- Row ${error.row}: ${error.message}\n`; | |
}); | |
console.error(errorMsg); | |
} | |
else { | |
theDataHeaders = theData[0]; | |
const commonProperties = ["UID","Name", "Documentation","Type","Specialization"]; | |
let missingColumns = commonProperties.filter(col => !theDataHeaders.includes(col)); | |
if (missingColumns.length > 0) { | |
let errorMsg = `The CSV is missing the following required columns: ${missingColumns.join(", ")}`; | |
console.error(errorMsg); | |
} | |
else { | |
let newGroupStart = 48; | |
let nameGroupStart = 180; | |
let uidGroupStart = 312; | |
const maxPerRow = 10; | |
let newGroupCount=0; | |
let nameGroupCount=0; | |
let uidGroupCount=0; | |
let newGroupRow=1; | |
let nameGroupRow=1; | |
let uidGroupRow=1; | |
const theDate = new Date(); | |
let theView = model.createArchimateView("CSVImport-"+theDate.toString()); | |
let templateObject = theView.createObject("note", 0, 0, -1, -1); | |
let templateHeight = templateObject.bounds.height; | |
let templateWidth = templateObject.bounds.width | |
templateObject.delete(); | |
let titleNote = theView.createObject("note", 12, 12, maxPerRow*templateWidth, 24); | |
titleNote.text = "Import from:"+filePath+" on "+theDate.toString(); | |
let newGroup = theView.createObject("diagram-model-group", 12, newGroupStart, maxPerRow*templateWidth, templateWidth); | |
newGroup.name = "New Elements"; | |
let nameGroup = theView.createObject("diagram-model-group", 12, nameGroupStart, maxPerRow*templateWidth, templateWidth); | |
nameGroup.name = "Matched by Name"; | |
let uidGroup = theView.createObject("diagram-model-group", 12, uidGroupStart, maxPerRow*templateWidth, templateWidth); | |
uidGroup.name = "Matched by UID"; | |
const commonProperties = ["UID","Name", "Documentation","Type","Specialization"]; | |
for (let i=1; i<theData.length; i++) { | |
let matchType = 3; | |
let theConcept = null; | |
let theObject = []; | |
for (let j=0; j<theDataHeaders.length; j++) { | |
theObject[theDataHeaders[j]]=theData[i][j]; | |
} | |
debug? console.log(theObject):true; | |
if (theObject.Name!=null && theObject.Name.trim()!="" && theObject.Name.trim()!="null") { | |
theConcept = $("#"+theObject.UID).first(); | |
if (!theConcept) { | |
debug? console.log("> Missing UID, checking Name"):true; | |
theConcept = $("."+theObject.Name).first(); | |
matchType=2; | |
if (!theConcept || theConcept.length>1) { | |
debug? console.log("> Creating Concept"):true; | |
theConcept = model.createElement(theObject.Type,theObject.Name); | |
matchType=1; | |
} | |
} | |
debug? console.log(theConcept):true; | |
theConcept.name=theObject.Name; | |
theConcept.documentation=theObject.Documentation; | |
theConcept.type=theObject.Type; | |
if (theObject.Specialization!="") | |
{ | |
theSpecializations = model.specializations; | |
for (let j=0; j<theSpecializations.length; j++) { | |
debug? console.log("> Checking Spec " + theSpecializations[j]):true; | |
if (theSpecializations[j].name==theObject.Specialization && theSpecializations[j].type == theObject.Type) | |
{ | |
debug? console.log("> Setting Spec " + theConcept.specialization):true; | |
theConcept.specialization=theObject.Specialization; | |
} | |
} | |
if (theConcept.specialization==null) | |
{ | |
debug? console.log("> Creating Spec " + theObject.Specialization):true; | |
model.createSpecialization(theObject.Specialization, theObject.Type); | |
theConcept.specialization=theObject.Specialization; | |
} | |
} | |
for (let j=0; j<theDataHeaders.length; j++) { | |
switch (theDataHeaders[j]) { | |
case "UID": | |
case "Name": | |
case "Documentation": | |
case "Type": | |
case "Specialization": | |
break; | |
default: | |
if (theObject[theDataHeaders[j]]) { | |
theConcept.prop(theDataHeaders[j],theObject[theDataHeaders[j]]); | |
} | |
else { | |
theConcept.removeProp(theDataHeaders[j]); | |
} | |
} | |
} | |
newGroup.bounds = {x:newGroup.bounds.x, y:newGroup.bounds.y, width:maxPerRow*templateWidth,height:(templateHeight+12)*newGroupRow+24}; | |
nameGroup.bounds = {x:nameGroup.bounds.x, y:newGroup.bounds.y+newGroup.bounds.height+12, width:maxPerRow*templateWidth,height:(templateHeight+12)*nameGroupRow+24}; | |
uidGroup.bounds = {x:uidGroup.bounds.x, y:nameGroup.bounds.y+nameGroup.bounds.height+12, width:maxPerRow*templateWidth,height:(templateHeight+12)*uidGroupRow+24}; | |
switch (matchType) { | |
case 3: | |
uidGroupCount++; | |
if (uidGroupCount>=maxPerRow) | |
{ | |
uidGroupRow++; | |
uidGroupCount=1; | |
} | |
theGroupStart = uidGroup.bounds.y; | |
theGroupCount = uidGroupCount; | |
theGroupRow = uidGroupRow; | |
break; | |
case 2: | |
debug? console.log("> Name Group Width " +nameGroup.bounds.width):true; | |
nameGroupCount++; | |
if (nameGroupCount>=maxPerRow) | |
{ | |
nameGroupRow++; | |
nameGroupCount=1; | |
} | |
theGroupStart = nameGroup.bounds.y; | |
theGroupCount = nameGroupCount; | |
theGroupRow = nameGroupRow; | |
debug? console.log("> NEW Name Group Width " +nameGroup.bounds.width):true; | |
break; | |
case 1: | |
newGroupCount++; | |
if (newGroupCount>=maxPerRow) | |
{ | |
newGroupRow++; | |
newGroupCount=1; | |
} | |
theGroupStart = newGroup.bounds.y; | |
theGroupCount = newGroupCount; | |
theGroupRow = newGroupRow; | |
break; | |
} | |
let theViewObject = theView.add(theConcept, (theGroupCount*templateWidth)-(templateWidth-12)+(theGroupCount*12), theGroupStart+12+((theGroupRow-1)*templateHeight)+(theGroupRow*12), -1, -1, true); | |
} | |
} | |
} | |
} | |
console.log("> Parsing Complete") | |
} | |
else { | |
console.log("> Cancelled"); | |
} |
Thanks a lot, @smileham! This update made my day ;-)
Hi Smileham, trying your export and import scripts and for some reason the relationships are not made while importing.
To test I just exported my view and reimported it in an empty model. All elements and their properties are exported/imported well, just the relationships not.
jArchi 1.6 enables support for CommonJS modules. To get this script to work replace the line:
load(__DIR__ + "lib/papaparse.min.js");
with:
const Papa = require(__DIR__ + "lib/papaparse.min.js");
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Well spotted, I've updated it to load the file using UTF-8, try it now! @degjerken