Showing posts with label reporting. Show all posts
Showing posts with label reporting. Show all posts

Wednesday, October 10, 2007

SECO Energy gives customers access to outage data

There is a good article in the September issue of Transmission & Distribution World titled Customers Access Online Outage Data. It was written by Barry Owens of SECO Energy. In case you missed it, Barry also gave the featured Customer Presentation at the recent Smallworld 2007 Americas Users Conference (presentations are here).

This is a project that I had heard about before and one of the things I had wondered was: "in a power outage situation, how will affected customers have the power to run their computers to check their outage status online?". Barry answers that question (and many others) in a well written presentation of their project.

Congratulations also to Peter Manskopf at GE for leading this successful project.

Wednesday, October 3, 2007

MUnit Testing Framework

I recently received a request from a reader wondering where the unit test application was on my blog. I don't have a post on that yet so I thought that now was as good a time as any to make one.

I'm not going to go into the details here. Suffice it to say, that the MUnit framework is based on the original JUnit framework but for Magik instead of Java.

I have found it helpful when developing code to create some kind of automated test scripts. There is much that can be debated about how much or how little test scripting to do.

Mark Field had a detailed presentation at the Smallworld 2007 Americas Users Conference this year titled "Implementing MUnit for Smallworld". I imagine you could contact him for the presentation or maybe we can get him to post more information on his blog. If you have the username/password, you can download the presentation from https://www.kinsleyassociates.com/ge/smallworld/presentations.asp

[NOTE: The above links are old. You can find the presentation slides and download zip file at http://sw-gis.wikidot.com/magik-munit]

GE also had/has a version of MUnit but from what I understand they are reluctant to release it to the community. I found that there was a version of MUnit created by Jan Vorel at http://www.janvorel.com. Unfortunately the current version of that site is not very user friendly like it used to be and you can no longer download Jan's code from there. But if you look at http://web.archive.org/web/20030406030601/www.janvorel.com/ you will find an archived version of the web site that still has a working link to his version of MUnit. It only works on CST 3.3 but I found that with a few modifications it works in CST 4.x as well.

The implementations of MUnit that I have seen do not allow you to test mouse/keyboard input. Mark's presentation describes how he enhanced MUnit to include those features.

One enhancement I did for the MUnit framework some time ago was to create a new test listener that exported the test results as XML and/or HTML. An example of those results is here.

You can click here for a Yahoo Groups search result on MUnit.

Friday, June 22, 2007

progress counters and dialogs

I recently had a colleague ask me how to pop up a progress bar dialog to indicate to a user the percentage complete of a process. I did not know how to answer that at first... Smallworld has historically not used progress bar dialogs as much as some users would expect. So I will present two options for showing progress status.

Text-based process

If you have some kind of text based process use progress_counter...


_block
_local some_collection << rope.new(20)

# create a progress_counter class that reports every 5
# increments. Because we know some_collection.size we can also
# estimate completion time
_local pc << progress_counter.new_on_count(5,some_collection.size)

_for idx,an_el _over some_collection.fast_keys_and_elements()
_loop
# the write() and sleep() statements are here to make a simple
# example...
write(an_el)
_thisthread.sleep(1000)

# ... but the :next() method is important to let the
# progress_counter (aka PC) know that you have completed one
# iteration of the loop.
pc.next()
_endloop
_endblock
$
unset
unset
unset
unset
unset
Done 5 (15 left) time taken 5 seconds 297 milliseconds (ETC. 15 seconds 891 milliseconds ) average rate 0.9439305267
unset
unset
unset
unset
unset
Done 10 (10 left) time taken 10 seconds 625 milliseconds (ETC. 10 seconds 625 milliseconds ) average rate 0.9411844826
unset
unset
unset
unset
unset
Done 15 (5 left) time taken 15 seconds 812 milliseconds (ETC. 5 seconds 270 milliseconds ) average rate 0.9487724325
unset
unset
unset
unset
unset
Done 20 (0 left) time taken 21 seconds 15 milliseconds (ETC. 0 milliseconds ) average rate 0.9562876356



... you can also ask the progress_counter to report at a given time interval...


_block
_local some_collection << rope.new(20)

# create a progress_counter class that reports every 2
# seconds. Because we know some_collection.size we can also
# estimate completion time
_local pc << progress_counter.new_on_time(3,some_collection.size)

_for idx,an_el _over some_collection.fast_keys_and_elements()
_loop
# the write() and sleep() statements are here to make a simple
# example...
write(an_el)
_thisthread.sleep(1000)

# ... but the :next() method is important to let the
# progress_counter (aka PC) know that you have completed one
# iteration of the loop.
pc.next()
_endloop
_endblock
$
unset
unset
unset
Done 3 (17 left) time taken 3 seconds 125 milliseconds (ETC. 17 seconds 708 milliseconds ) average rate 0.9600000000
unset
unset
unset
Done 6 (14 left) time taken 6 seconds 250 milliseconds (ETC. 14 seconds 583 milliseconds ) average rate 0.9600000000
unset
unset
unset
Done 9 (11 left) time taken 9 seconds 359 milliseconds (ETC. 11 seconds 439 milliseconds ) average rate 0.9616468318
unset
unset
unset
Done 12 (8 left) time taken 12 seconds 453 milliseconds (ETC. 8 seconds 291 milliseconds ) average rate 0.9648530373
unset
unset
unset
Done 15 (5 left) time taken 15 seconds 578 milliseconds (ETC. 5 seconds 199 milliseconds ) average rate 0.9616468318
unset
unset
unset
Done 18 (2 left) time taken 18 seconds 703 milliseconds (ETC. 2 seconds 81 milliseconds ) average rate 0.9612351238
unset
unset
Done 20 (0 left) time taken 20 seconds 765 milliseconds (ETC. 0 milliseconds ) average rate 0.9612351238



If you want to use a more user-friendly indicator, use the progress_indicator_dialog. It is loaded into the core 4.1 image and you can find details about it in the header of product/modules/sw_core/progress_manager/source/progress_indicator_dialog.magik


_block
_local q << progress_indicator_dialog.new ( "Generating" )
q.interrupt_handler << handler.new ( _unset, _unset )
q.info_string << "The current series will contain approx 10 frames."
q.bar_label << "Generating frames..."
q.image << { :help, _unset }
q.activate ()

q.max_count << 10

_for n _over range(1,10,1)
_loop
_thisthread.sleep(1000)

q.progress_changed(n)
_endloop
_endblock
$


And there you have it... easy ways to let your users know the progress of a process.

Monday, May 14, 2007

Magik OLE Excel Reporting Performance Enhancements

I recently received a question about how to speed up Magik reporting to Excel using the OLE functionality. I you have ever used OLE from Magik to access other APIs you will know that it is a powerful but not overly fast way of connecting to 3rd party applications.

The questioner had indicated that they were writing data from Magik to Excel one cell at a time and then formatting each cell individually, too. While this is a logical way of doing a report it was taking a very long time to create a typical customer report.

My suggestions to improve data transfer and formatting performance were:

  • If your report is basically a row report, then you can copy one row at a time onto the Windows text clipboard in Magik. Then you can use an OLE call on Excel to paste the copied text into a range of cells. Be sure that the copied text has a delimiter that Excel can parse on. That way you can pass a large amount of data from Magik to Excel with a single OLE call.


_block
_constant delim_char << tab_char
_local a_frame << smallworld_product.applications.an_element().top_frame

# using a text output stream will let you cleanly create
# a delimited string later on.
_local temp_stream << internal_text_output_stream.new()

_for a_record _over records.fast_elements()
_loop
# using OLE, set your cell range...

data_list {a_record.attr1,a_record.attr2,a_record.attr3}
temp_stream.reset()
temp_stream.write_list_with_separator(data_list,delim_char)

a_frame.clipboard_text << temp_stream.string

# using OLE, do a "Paste" into the current cell
_endloop
_endblock
$
  • As for formatting: is there similar formatting for a row or column of cells? If there is, then keep track of how many rows or columns you have as you are copying/pasting the data into Excel. Then, once you have finished getting the data into Excel, format a range of columns or a range of rows or a range of cells that require common formatting. Again, that way you do the formatting with a minimum number of OLE calls
  • Have you looked at the Smallworld Explorer “Export to Excel” functionality? I think that core code (4.0 SWAF) might make use of an Excel template.
    • see collection_export_engine.export_to_excel