Creating XML file of folder directory hierarchy - visual-foxpro

I am trying to create an XML file that represents the hierarchical directory structure with an unknown amount of nested directories and files. This question relates to a previous one asked by myself, How to loop through nested directories in Foxpro given the initial start directory, so the code I have so far (see below) is based on the structure from the answer here.
*********************************
**** Variable initialization ****
*********************************
set asserts on
set date ymd
public loXml as MSXML2.DOMDocument60
public loRoot as MSXML2.IXMLDOMElement
public loNode as MSXML2.IXMLDOMElement
public loFile as MSXML2.IXMLDOMElement
public loFolder as MSXML2.IXMLDOMElement
public loSubFolder as MSXML2.IXMLDOMElement
public lcRootDir as String
public lcManifestNS as String
lcRootDir = "C:\LoopTest" && user defined root directory
lcManifestNS = "http://www.randomnamespace.com/ServerManifest/1.1" && namespace which all elements int his document reside
***********************************
**** Creating the XML Document ****
***********************************
loXml = createobject("MSXML2.DOMDocument.6.0")
loRoot = loXml.createNode("Element", "au:Manifest", lcManifestNS) && root element
loRoot.setAttribute("Path", lcRootDir) && path attrubute of root element
GetFilesRecursively(lcRootDir) && builds all folder and file child nodes under the root element
loXml.appendChild(loRoot) && append everything within the root node to the document
*********************
** same xml output **
*********************
loXml.save('C:\results\ex2_manifest_test.xml') && save the xml file to desired location
***************
** Functions **
***************
procedure getfilesrecursively(tcfolder)
assert ( vartype(m.tcfolder) = 'C' )
local lnfile, lafiles[1], lcfileorfolder, datemod && create local variables
for lnfile = 1 to adir(lafiles, addbs(m.tcfolder) + '*', 'd') && loop that will go through all direct items within initial directory
lcfileorfolder = lower(lafiles[m.lnfile, 1]) && this variable is the name of the item inside directory
if empty( strtran(m.lcfileorfolder,'.') ) && some items have names which are just . or .. etc, so ignore these items by restarting the loop
loop
endif
datemod = strtran(dtoc(lafiles[lnfile, 3]), '/', '-') + "T" + lafiles[lnfile, 4] && creating the datetime last modified value
if directory( addbs(m.tcfolder)+m.lcfileorfolder, 1 ) && if there is a subdirectory, re-run the main loop with updated file path
loFolder = loRoot.appendChild(loXml.createNode("Element", "au:Folder", lcManifestNS)) && folder node under root node
loFolder.setAttribute("Name", lcfileorfolder) && attrubutes for folder node
loFolder.setAttribute("Date", datemod)
getfilesrecursively(addbs(m.tcfolder)+m.lcfileorfolder)
else && if no further subdirectory, then insert the item into table
loFile = loFolder.appendChild(loXml.createNode("Element", "au:File", lcManifestNS)) && file node under folder node
loFile.setAttribute("Name", lcfileorfolder) && attributes for file node
loFile.setAttribute("Date", datemod)
endif
endfor
endproc
The main problem I am running into with this code is that all the folder directories are getting put under the root node and are not nesting properly. I understand why, but I do not clearly see how I can change this. Would it be to essentially create a new node every iteration and then append to that? I am just getting used to the syntax around FoxPro XML so some help fixing this issue would be much appreciated. In addition, it seems that any files under the root directory get put inside one of the nested directories in the XML output. You can probably see why as per my code. So ultimately, I am just looking to correct these things to get the XML output nodes to be properly nested. Maybe there is a different approach at solving this, so feel free to answer this question with an entirely new code outline if you wish.

You could do it using ADIR() as follows (please read comments following code):
Local lcOutputFile, lcRoot
lcOutputFile = 'C:\temp\nestedDirs.xml'
lcRoot = Home()
GetTreeXML(m.lcRoot, m.lcOutputFile)
Procedure GetTreeXML(tcRootPath, tcOutput)
Local lcXML
Set Textmerge On To (m.tcOutput) Noshow
\<au:Manifest xmlns:au="http://www.randomnamespace.com/ServerManifest/1.1" Path="<< ADDBS(m.tcRootPath) >>">
GetTree(m.tcRootPath,'myDirectories',0)
*!* Select * From myDirectories &&ORDER BY Filepath
*!* Use In 'myDirectories'
\</au:Manifest>
Set Textmerge Off
Set Textmerge To
Return m.lcXML
Endproc
Procedure GetTree(tcPath, tcCursorName, tnLevel)
Local lcCurDir, ix, lcPath
Local Array laDirs[1]
lcCurDir = Addbs(m.tcPath)
If m.tnLevel = 0
Create Cursor (m.tcCursorName) (FilePath c(250), Level i)
Endif
Insert Into (m.tcCursorName) (FilePath,Level) Values (m.lcCurDir, m.tnLevel)
For ix = 1 To Adir(laDirs,m.lcCurDir+"*.*","DHS")
lcPath = m.lcCurDir+laDirs[m.ix,1]
If laDirs[m.ix,1]#"." And "D"$laDirs[m.ix,5]
GetTree(m.lcPath, m.tcCursorName, m.tnLevel+1)
Else
If laDirs[m.ix,1] != '..'
ProcessPath( m.lcPath, m.tnLevel, laDirs[m.ix,3], laDirs[m.ix, 4] )
Endif
Endif
Endfor
\<< SPACE(2 * m.tnLevel) >></au:Folder>
Endproc
Procedure ProcessPath(tcPath, tnLevel, tdFileDate, tcFileTime)
Local lcName, lcDate
lcDate = Ttoc( Cast(m.tdFileDate As Datetime) + (Ctot(m.tcFileTime)-Ctot('0')), 3)
If Right(m.tcPath,2) == '\.' && Directory
If m.tnLevel > 0 && Skip root
lcName = Justfname(Left(m.tcPath, Len(m.tcPath)-2))
\<< SPACE(2 * m.tnLevel) >><au:Folder Name="<< m.lcName >>" Date="<< m.lcDate >>">
Endif
Else
lcName = Justfname(m.tcPath)
\<< SPACE(2 * (m.tnLevel+1)) >><au:File Name="<< m.lcName >>" Date="<< m.lcDate >>"/>
Endif
Endproc
I'am not sure about the resulting XML. I followed the format of yours. To me it looks a liitle awkward but if that is the format of your manifest then it is fine (I couldn't browse the schema).
Unrelated, in your code there were variables declared as public, please forget that there is a 'public' in VFP. Almost all of us use it only for the specific 'oApp' object created at the start of an application if any. Believe me it is dangerous to use.
Note: VFP ships a DLL called Filer.dll. It contains an activex 'Filer.FileUtil'. It can collect folder and files with some filters applied (has more than simple *.*' file skeleton filter). And it returns the results with original casing, unlike adir() result which uppercases all. Also, you can get file creation, modification and last access times.
Another one is Scripting.FileSystemObject. However, due to security it might not be installed.
If Adir() is fine for you then it is simple and fast.
EDIT: I later noticed above code would add an extra au:Folder at the end plus it wouldn't handle characters that are not allowed in XML. DOM processing would take care of both. So here is the version with DOM:
Local lcOutputFile, lcRoot
lcOutputFile = 'C:\temp\nestedDirs.xml'
lcRoot = Home()
GetTreeXML(m.lcRoot, m.lcOutputFile)
Procedure GetTreeXML(tcRootPath, tcOutput)
Local loXML As "MSXML2.DOMDocument.6.0", loRoot As "MSXML2.IXMLDOMElement"
loXML = Createobject("MSXML2.DOMDocument.6.0")
loRoot = m.loXML.createNode("Element", "au:Manifest", "http://www.randomnamespace.com/ServerManifest/1.1")
loRoot.setAttribute("Path", m.tcRootPath)
GetTree(m.tcRootPath,'myDirectories',0, m.loXML, m.loRoot)
m.loXML.appendChild(m.loRoot)
m.loXML.Save(m.tcOutput)
Endproc
Procedure GetTree(tcPath, tcCursorName, tnLevel, toDOM, toParent)
Local lcCurDir, ix, lcPath, lcName, lcDate, loElement
Local Array laDirs[1]
lcCurDir = Addbs(m.tcPath)
For ix = 1 To Adir(laDirs,m.lcCurDir+"*.*","DHS")
lcName = laDirs[m.ix,1]
lcDate = Ttoc( Cast(laDirs[m.ix,3] As Datetime) + (Ctot(laDirs[m.ix,4])-Ctot('0')), 3)
If laDirs[m.ix,1]#"." And "D"$laDirs[m.ix,5]
lcPath = m.lcCurDir + laDirs[m.ix,1]
loElement = m.toDOM.createElement("au:Folder")
m.loElement.setAttribute("Name", m.lcName)
m.loElement.setAttribute("Date", m.lcDate)
m.toParent.appendChild(m.loElement)
GetTree(m.lcPath, m.tcCursorName, m.tnLevel+1, m.toDOM, m.loElement)
Else
If laDirs[m.ix,1] != '.' And laDirs[m.ix,1] != '..'
loElement = m.toDOM.createElement("au:File")
m.loElement.setAttribute("Name", m.lcName)
m.loElement.setAttribute("Date", m.lcDate)
m.toParent.appendChild(m.loElement)
Endif
Endif
Endfor
Endproc

Related

Change MailSession in WebSphere for HCL Connections with change detection if anything needs to be modified or was modified

I want to modify the default lcnotification mail session of HCL Connections 6.5/7 in WAS. Since this is automated with Ansible, I'd like to detect if the desired settings are still there or need to be set - including a full sync of the nodes in case anything was modified. I'm struggeling how to detect this properly, since neither AdminConfig.modify nor AdminConfig.save tell me if anything was changed or not.
So I tried to fetch the MailSession object and check if each of my properties I'd like to set matches:
properties = [
["mailTransportHost", "{{ mail_host }}"],
["mailTransportUser", "{{ mail_user }}"],
["mailTransportPassword", "{{ mail_pw }}"],
["mailFrom", "{{ mail_sender }}"],
["debug", "{{ mail_debug | lower }}"]
]
session = AdminConfig.list('MailSession', 'lcnotification*')
existing = AdminConfig.showall(session)
isModified = 0
for line in existing.splitlines():
noBrackets = line[1:-1]
firstSpace = noBrackets.index(" ")
key = noBrackets[0:firstSpace]
val = noBrackets[firstSpace:].strip()
# print key + " -> " + val
for prop in properties:
propKey = prop[0]
propVal = prop[1]
if propKey == key and val != propVal:
print(propKey + " not maching:\n\tPresent: " + val + "\n\tWanted: " +
propVal)
isModified = 1
if isModified:
AdminConfig.modify(session, properties)
AdminConfig.save()
import shared
shared.synchAllNodes()
In the Ansible call, I use changed_when to check if not maching is present in stdout. This doesn't work properly, because WAS return stars instead of the mailTransportPassword - so I cannot check if it matches:
stdout: |-
WASX7209I: Connected to process "dmgr" on node CnxCell-dmgr using SOAP connector; The type of process is: DeploymentManager
mailTransportPassword not maching:
Present: *****
Wanted: dummypw
Syncronizing nodeCnxNode01
-----------------------------------------------------------------------------------------
Full Resyncronization completed
I see no clean way to fix this. Is there another (maybe even cleaner) way, to see if anything was modified and a full re-synchronisation needs to be done?
I had a look at the synchronisation state of the node and it seems that WAS detects correctly if there are changes which needs to be synced. So I'm writing my properties and check the sync node. If it is out of sync, something was changed -> We need to do a full sync. If Modified is printed to stdout, Ansible can mark the task as changed.
AdminConfig.modify(session, properties)
AdminConfig.save()
node = AdminControl.completeObjectName('type=NodeSync,*')
isSynced = AdminControl.invoke(node, 'isNodeSynchronized')
print node + "\n\tIs synced: " + isSynced
# Nasty, but the old included Python of WAS still can't handle boolean values
if isSynced == "false":
# For the change detection
print "Modified, starting full sync"
import shared
shared.synchAllNodes()

JXA: Create a mailbox in Apple Mail

I am trying to create a sub-mailbox in Apple Mail using JavaScript.
I have the following code snippet (parent is a reference to the mailbox in which I want the new mailbox):
var mb = mail.Mailbox({name: "SubFolder"});
parent.mailboxes.push(mb);
The events log shows:
app = Application("Mail")
app.mailboxes.byName("Local").mailboxes.byName("Archive").mailboxes.push(app.Mailbox({"name":"SubFolder"}))
--> Error -10000: AppleEvent handler failed.
What am I doing wrong?
Thanks,
Craig.
Code now:
var mb = mail.Mailbox({name: "Local/Archive/Test Archive/SubFolder"})
logger.logDebug("mb = '" + Automation.getDisplayString(mb) + "'.");
mail.mailboxes.push(mb) // create the subfolder
This works as long as there are no spaces in the path.
I tried to force the space using \\ in front of it, but then you get "Test\ Archive" as the name.
So how do I get a space in the name to work?
Thanks.
To create a sub-folder, you need a name like a posix path --> "/theMasterMailbox/subMailBox1/subMailBox2/subMailBox3".
So, you need:
A loop to put the name of each parent folder into an array.
Use join('/') to join the elements of an array into a string.
Use mail.mailboxes.push(mb) instead of parent.mailboxes.push(mb)
Here's a sample script which creates a mailbox named "SubFolder" in the selected folder (the mailbox):
mail = Application('com.apple.Mail')
parent = mail.messageViewers()[0].selectedMailboxes()[0]
mboxNames = [parent.name()]
thisFolder = parent
try {
while (true) { // loop while exists the parent folder
mboxNames.unshift(thisFolder.container().name()) // add the name of the parent folder to the beginning of an array
thisFolder = thisFolder.container() // get the parent of thisFolder
}
} catch (e) {} // do nothing on error, because thisFolder is the top folder
mboxNames.push("SubFolder") // append the name of the new subFolder to the array
mBoxPath = mboxNames.join('/') // get a string (the names separated by "/")
mb = mail.Mailbox({name:mBoxPath})
mail.mailboxes.push(mb) // create the subfolder

Updating A Program's Directory

I have a program that uses forms. The forms are pointing to files in c:\filepath1 as set forth in main.prg (set defa, set path). I make a test copy of the files, change the path in main.prg, the forms are still referencing the files in the old path. I don't want to have to recreate all the data environments in all the forms. How can I avoid doing so? Any help on this would be kindly appreciated.
Missy.
When Dataenvironment is used AND the path recorded in DataEnvironment tables exist, then that path is used no matter what the current path is. If you don't want to touch Dataenvironment at all (even programmatically) then you must remove that path (rename for example).
As a side note: I have:
UpdateDE(this)
in my DataEnvironment.BeforeOpenTables method. In UpdateDE.prg I have code that loops all the cursors in DE and set their path to the one that I want to use as "current".
EDIT: Here is a sample UpdateDE.prg:
Lparameters toDE
Do setups && prg keeping common "set" entries
Local Array aDEMembers[1]
Local lnMembers,ix,lcMembers
If !(Type('oApp')='O' And !Isnull(m.oApp))
Public oApp
oApp = Createobject('myApp')
Endif
lnMembers = Amembers(aDEMembers,m.toDE,2)
For ix=1 To m.lnMembers
With Evaluate('toDe.'+aDEMembers[m.ix])
If Lower(.BaseClass) == 'cursor'
If Atc(oApp.cAppDBC,.Database) > 0
.Database = Addbs(oApp.cAppDataPath)+oApp.cAppDBC
Else
.CursorSource = Addbs(oApp.cAppDataPath)+Justfname(.CursorSource)
Endif
Endif
Endwith
Endfor
Define Class myApp As Custom
cAppDBC = 'myDatabase.dbc'
cAppDataPath=Fullpath('data')
cCurPath = ''
Procedure Init
This.cCurPath = Set('path')
If File('dbparam.dbf') && a small dbf that holds path to current data folder
Select dataLoc From dbparam Where locType == 'DATABASE' Into Array arrDataLoc
If _Tally > 0
This.cAppDataPath = arrDataLoc
Set Path To (arrDataLoc[1]+';'+This.cCurPath)
Endif
Use In 'dbparam'
Endif
Endproc
Procedure Destroy
Set Path To (This.cCurPath)
Endproc
Enddefine
I ended up renaming the default path then going through all the screens in the program then recompiling. Thank you for your help. I did understand it and is probably another good solution.

Import Multiple Images With Unknown Names

I need to import multiple images (10.000) in Matlab (2013b) from a subdirectory of the predefined directory of Matlab.
I don't know the exact names of the images.
I tried this:
file = dir('C:\Users\user\Documents\MATLAB\train');
NF = length(file);
for k = 1 : NF
img = imread(fullfile('C:\Users\user\Documents\MATLAB\train', file(k).name));
end
But it throws this error though I ran it with the Admin privileges:
Error using imread (line 347)
Can't open file "C:\Users\user\Documents\MATLAB\train\." for reading;
you may not have read permission.
The "dir" command returns the virtual directory elements "." (self directory) and ".." parent, as your error message shows.
A simple fix is to use a more specific dir call, based on your image types, perhaps:
file = dir('C:\Users\user\Documents\MATLAB\train\*.jpg');
Check the output of dir. The first two "files" are . and .., which is similar to the behaviour of the windows dir command.
file = dir('C:\Users\user\Documents\MATLAB\train');
NF = length(file);
for k = 3 : NF
img = imread(fullfile('C:\Users\user\Documents\MATLAB\train', file(k).name));
end
In R2013b you would have to do
file = dir('C:\Users\user\Documents\MATLAB\train\*.jpg');
If you have R2014b with the Computer Vision System Toolbox then you can use imageSet:
images = imageSet('C:\Users\user\Documents\MATLAB\train\');
This will create an object containing paths to all image files in the train directory, regardless of format. Then you can read the i-th image like this:
im = read(images, i);

Rally APIs: How to Move A Test Folder

I've worked with the script outlined in the following answer:
Rally APIs: How to copy Test Folder and member Test Cases
and it's handy, but what I really want to do is to move an entire Test Folder into a different project. This is next to impossible through the Rally User Interface. According to Rally Support, the only way to do this in the UI is:
Un-assign the Test Cases from their current Test Folder
Setup a Custom Grid app on your dashboard
Use the Custom Grid bulk edit to update the Project of the Test Cases
Lastly use the Custom Grid bulk edit to update the Test Folder - now that you're in the target Project, of the Test Cases
Even though the above process is clunky, it is easier now than it used to be before the advent of the bulk edit within the Custom Grids. Before you had to go through and edit each Test Case one-by-one which was very manual and slow.
However, we have several thousand Test Cases we need to move, and the Custom Grid has a fatal flaw for us. It will only show the first 200 records in a query. So we would have to manually change our grid query in a step wise manner to accomplish the move we need. This is barely better than editing Test Cases one-by-one. Is there a way to move a Test Folder with Test Cases from one Project to another, using a script? Please tell me there is.
The following script will perform this task - it will move all Test Cases from a Source Test Folder identified by FormattedID, to a Target Test Folder, also identified by FormattedID. The Source Test Folder and Target Test Folder can be in different Projects (although they must be within the same Workspace). Like the Copy script referenced in the question, the Target Test Folder must exist, i.e. the script will not create a Test Folder for you if the Target is not found.
For those needing to install and configure the Ruby REST Toolkit, links are here:
Developer Portal: Rally REST API for Ruby
Github
# Copyright 2002-2012 Rally Software Development Corp. All Rights Reserved.
require 'rally_api'
$my_base_url = "https://rally1.rallydev.com/slm"
$my_username = "user#company.com"
$my_password = "password"
$my_workspace = "My Workspace"
$my_project = "My Project"
$wsapi_version = "1.39"
# Test Folders
$source_test_folder_formatted_id = "TF8"
$target_test_folder_formatted_id = "TF11"
#==================== Make a connection to Rally ====================
config = {:base_url => $my_base_url}
config[:username] = $my_username
config[:password] = $my_password
config[:workspace] = $my_workspace
config[:project] = $my_project
config[:version] = $wsapi_version
#rally = RallyAPI::RallyRestJson.new(config)
begin
# Lookup source Test Folder
source_test_folder_query = RallyAPI::RallyQuery.new()
source_test_folder_query.type = :testfolder
source_test_folder_query.fetch = true
source_test_folder_query.query_string = "(FormattedID = \"" + $source_test_folder_formatted_id + "\")"
source_test_folder_result = #rally.find(source_test_folder_query)
# Lookup Target Test Folder
target_test_folder_query = RallyAPI::RallyQuery.new()
target_test_folder_query.type = :testfolder
target_test_folder_query.fetch = true
target_test_folder_query.query_string = "(FormattedID = \"" + $target_test_folder_formatted_id + "\")"
target_test_folder_result = #rally.find(target_test_folder_query)
if source_test_folder_result.total_result_count == 0
puts "Source Test Folder: " + $source_test_folder_formatted_id + "not found. Exiting."
exit
end
if target_test_folder_result.total_result_count == 0
puts "Target Test Folder: " + $target_test_folder_formatted_id + "not found. Target must exist before moving."
exit
end
source_test_folder = source_test_folder_result.first()
target_test_folder = target_test_folder_result.first()
# Populate full object for both Source and Target Test Folders
full_source_test_folder = source_test_folder.read
full_target_test_folder = target_test_folder.read
# Grab collection of Source Test Cases
source_test_cases = source_test_folder["TestCases"]
# Loop through Source Test Cases and Move to Target
source_test_cases.each do |source_test_case|
begin
test_case_to_update = source_test_case.read
source_test_case_formatted_id = test_case_to_update["FormattedID"]
target_project = full_target_test_folder["Project"]
target_project_full_object = target_project.read
target_project_name = target_project_full_object["Name"]
source_project = full_source_test_folder["Project"]
source_project_full_object = source_project.read
source_project_name = source_project_full_object["Name"]
puts "Source Project Name: #{source_project_name}"
puts "Target Project Name: #{target_project_name}"
# Test if the source project and target project are the same
source_target_proj_match = source_project_name.eql?(target_project_name)
# If the target Test Folder is in a different Project, we have to do some homework first:
# "un-Test Folder" the project
# Assign the Test Case to the Target Project
# Assign the Test Case to the Target Test Folder
if !source_target_proj_match then
fields = {}
fields["TestFolder"] = ""
test_case_updated = #rally.update(:testcase, test_case_to_update.ObjectID, fields) #by ObjectID
puts "Test Case #{source_test_case_formatted_id} successfully dissociated from: #{$source_test_folder_formatted_id}"
# Get full object on Target Project and assign Test Case to Target Project
fields = {}
fields["Project"] = target_project_full_object
test_case_updated = #rally.update(:testcase, test_case_to_update.ObjectID, fields) #by ObjectID
puts "Test Case #{source_test_case_formatted_id} successfully assigned to Project: #{target_project_name}"
end
# Change the Test Folder attribute on the Test Case
fields = {}
fields["TestFolder"] = target_test_folder
test_case_updated = #rally.update(:testcase, test_case_to_update.ObjectID, fields) #by ObjectID
puts "Test Case #{source_test_case_formatted_id} successfully moved to #{$target_test_folder_formatted_id}"
rescue => ex
puts "Test Case #{source_test_case_formatted_id} not updated due to error"
puts ex
end
end
end

Resources