Helpstream sends updates to Twitter (and anything else)

I just added a new business rules action which lets Helpstream push real-time updates to external systems.  One example of this is to push real-time updates from Helpstream to Twitter - for example, whenever a new post is made in the Community, or a new Case is open or resolved.  Of course, Helpstream has its own "subscribe" features, but if you want people to be able to "follow" your Helpstream workspace on Twitter, with whatever events please you, this is now possible!

It's simple really - for the new 'Http or Https request' action, enter expressions for the URL, method, content-type, body, and optionally user name and password (for HTTP Basic Auth).  To integrate with Twitter, for example, posting an update with a case summary, you would enter

url - 'https://twitter.com/statuses/update.xml?status=New+case:++' + urlEncode($summary)

method - 'POST'

user - 'myusername@company.com'

password - 'twitterpassword'

This  will post a "New case:  [case summary]' message to Twitter, as real-time as possible.  If real-time is not possible due to current load, the update should happen within a minute or so.

Twitter has a simple REST api, with HTTP basic authentication, so it can all happen in one request.  For other systems, at worst you'd have to host a web page (PHP, Java servlet, etc.), and have that act as a proxy that converted the request into whatever was needed.

 

 

Posted on Tuesday, July 1, 2008 at 06:58PM by Registered CommenterDan Hardy in | CommentsPost a Comment

What I like about Office 2007

I've been using Office 2007 for a while.  Honestly, my initial reaction was:

1) things seemed harder to find - for example, how to you get to the Visual Basic macro editor in Excel 2007? 
2) why would you use the new formats - docx, xlsx, and pptx?  I figured for years you would have to save them back to 97-2003 format if you wanted anybody else to read them.

I finally found out why #2 is an immediate benefit to me.  Not to me as an end user, but as a software vendor. The sooner the 97-2003 format is gone, the better.  (Yes, I'm sure the answer to that is "never", but one can hope).


Helpstream fully indexes binary documents that are in the knowledge base, are attached to cases or case history, or are attached to community discussion.  Given our 100% Java/Linux architecture, the proprietary Office 97-2003 format documents present an annoying technical challenge.  In past products, I've done as Joel Spolsky suggests here (see "Let Office do the heavy work for you").  I've written C++/ATL code to automate the Office applications, extracting the text to feed into Lucene.  This requires either running your app on Windows and using JNI from your Java app, or throwing a Windows box in the data center and making an RPC/Web Service network request.  I went the JNI route in the past, and it worked well.

For Helpstream however, I wanted to keep a 100% Linux architecture, and still generate and index Office documents.  Generation is pretty easy - for Word you can use open source packages to generate RTF.  Extracting the text form the proprietary format is still a challenge.  For Office 97-2003 docs, after some Google searching, I found an obscure 100% pure Java library from a commercial vendor named Davisor which allowed me to convert the documents to XML, and therefore extract the text.

With Office 2007 files, there is a new opportunity.  Indexing those documents in Java becomes trivial!  The .docx, .pptx, .and xlsx files are zip files that contain a bunch of files, among them some xml files.  The following code will give you good input to Lucene for any Office 2007 document from pure Java:


public void indexOffice2007Document(InputStream inputStream, Writer writer) throws Exception {
    ZipInputStream zis = new ZipInputStream(inputStream);
    ZipEntry zi = null;
    while ((zi = zis.getNextEntry()) != null) {
        String file = zi.getName();
        if (file != null && file.toLowerCase().endsWith(".xml")) {
                    File tempFile = File.createTempFile("tmp", ".xml");
                    try {
                        byte [] chunk = new byte[8096];
                        int bytesRead = 0;
                        FileOutputStream fs = new FileOutputStream(tempFile);
                        while ((bytesRead = zis.read(chunk)) != -1) {
                            fs.write(chunk, 0, bytesRead);
                        }
                        fs.close();
                        // index it
                        try {
                            Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(tempFile);
                            NodeIterator iter = XPathAPI.selectNodeIterator(doc.getDocumentElement(), "//text()");
                            Node node = null;
                            while ((node = iter.nextNode()) != null) {
                                String val = ((Text)node).getTextContent();
                                if (val != null && val.length() > 0) {
                                    writer.write(val);
                                    writer.write(" ");
                                }
                            }
                        } catch (Throwable t) {
                        // ignore XML parse errors -- log if you want to
                        }
                    } finally {
                        tempFile.delete();
                    }
        }
    }
}


Now, things are easy!  No 3rd party licensing issues or support/upgrades to deal with, and no need to deploy a Windows box at all.  And no need to dig up your C++/ATL skills that you had hoped you were done with for good when you last used them years ago.  And I'm certain it scales better than automating Office COM object (which are probably running out-of-process).

 

Posted on Friday, June 20, 2008 at 09:34AM by Registered CommenterDan Hardy in | CommentsPost a Comment

Cloud Computing with Persistent Storage

 
This post from Amazon Web Services caught my attention: 

http://aws.typepad.com/aws/2008/04/block-to-the-fu.html

With the ability to dynamically create S3 storage and mount it as a file system on your EC2 instances, virtualization and pay-for-usage is likely to become a great option for hosting of SaaS applications.

Looking at the pricing, it seems like a couple of application servers, a database server, and a ton of persistent storage can be had for right around $1000/month.  That really changes the economics of deploying an app, particularly for early stage companies.  Even if you disregard the capital investment for hardware that is avoided (which could be tens of thousands), that's a very competitive monthly operations fee.

It will be really interesting to try this out when it is available later this year.

Posted on Tuesday, April 15, 2008 at 10:52AM by Registered CommenterDan Hardy | Comments1 Comment

Advanced Features - Business Rules (Part 2)

Last time I introduced one of our more advanced features - the Business Rules Engine - and gave some examples of its uses.   Here I'll discuss the design and implementation of the engine itself.

The core technical bit of the business rules engine is the expression evaluator.  When defining business rules, you need to be able to describe what condition triggers the rule, when it should execute, and what it should do.  Each of these is defined by providing an expression.

The expression syntax is really a programming language of sorts.  In fact, when designing this part of the product, that's exactly how I approached it.  I needed to invent a grammar, or language, that supported many of the standard constructs in any other language, but also had special syntax for working with Helpstreams' data (business objects and their properties, useful functions, etc.).

I started by coming up with some use cases I knew we would need to support, and then writing what I thought each expression should look like.  This let me take a "top down" approach, and choose whatever syntax I thought was most capable and usable.  For example, to test whether a status has changed to "Resolved" in an update, I though you would write something like this:

$old.status != $status and $status = "Resolved"

The first part tests whether the status property is different from what is in the database (the "old" value).  The second part tests whether the new value of the status property is Resolved.  This seemed pretty straightforward.  I tried to make the syntax pretty easy for developers familar with almost any language, and was targeting "someone who can write an Excel spreadsheet formula" as about the expected skillset of the audience.

Of course, you don't want to reinvent the wheel, so things like operator precedence (and/or, grouping with parentheses, +- vs */) should work like other languages such as Java.

To define the grammar and write the evaluator, I turned to the ANTLR open source package. 

ANTLR is an excellent example of how the best-of-breed open source packages provide a huge benefit to software developers today.  Take a look at the number of grammars that have been defined using it!

The entire Helpstream grammar is defined in a relatively short file (~300 lines), and ANTLR generates the lexer and parser classes.  These classes are over 18,000 lines of Java code!  I sure wouldn't want to write that by hand.  During evaluation, the parser calls into several evaluator classes I wrote to handle things like object property references, functions, etc.

In the end, you can write extremely sophisticated expressions, and it isn't any harder than a spreadsheet formula.  For example, to trigger a rule if it is between 9 and 5, California time, AND the priority is High OR the summary contains the word "Emergency", AND the Account associated with the requester has a valid support contract (a custom field we'll assume is defined on Account):

(hour(date()+tzOffset(date(), "PST")) >= 9 AND  hour(date()+tzOffset(date(), "PST")) < 17) AND

($priority = 'High' OR fullTextSearch($summary, "Emergency~")) AND

$requester.accountCustom.supportValid = 'Yes'

This uses a few functions - hour, date, tzOffset, and fullTextSearch (which uses the excellent Lucene open source package) - and demonstrates referencing object properties, including related objects and custom fields.

To take an action such as assigning this ticket automatically, you might add AND $assignee = null to the condition, and set field 'assignee' to the expression:

query("Person", "record.emailAddress = 'bob.backline@mycompany.com'")

The "query" function in this case will find the appropriate Person to assign to the 'assignee' field based on their email address.

By combining all of the customization features available in Helpstream (custom fields, custom notifications, business rules, reporting, branding), you can really tailor the solution to fit your specific needs - easily, without complex programming, and without installing any software.

~Dan

 

Posted on Monday, November 26, 2007 at 11:01AM by Registered CommenterDan Hardy in | Comments1 Comment

Advanced Features - Business Rules (Part 1)

We try hard to make Helpstream as easy to use as possible, so you can get up and running in minutes, without any training at all.

Under the hood, however, there are rich capabilities that are probably not obvious when you first sign up.  In this entry, I'll give a brief introduction to our Business Rules engine - one of of our most powerful, and most technical, components.  In future entries, I'll drill down into more technical details.

The Business Rules engine is designed to configure your Helpstream support desk according to your own workflow and rules.  Do you want tickets automatically assigned (based on problem type, priority, requester's account support level, geography, day of week, etc.)?  Business rules does that.  Do you want to be notified if a particular type of case is open for more than X hours?  Business rules does that too. 

Business rules are also completely integrated with the Custom Field engine, so your rules can be based on your custom attributes on Account, Case, or Person.

Each Business Rule boils down to three things:

1) Condition.  What causes this rule to run?  The condition is evaluated on every create/update transaction for a business object type.

2) Date.  Do the actions run immediately and transactionally, or are they scheduled for a future date?

3) Actions.  What should happen?  This can consist of updates to fields (such as assignment, automatic prioritization), email notifications (using customizable email templates), and data validation (presenting error messages and preventing a transaction).

I'll get into more technical details in the next entry, but for now here's an example.  This action assigns all networking tickets to Norm Network if they are created without an assignee.

Condition: $old.id = null and $assignee = null and $problemType = 'Networking'

Date: empty (now)

Action: Assignment.  Field: 'assignee'.  Value: query('Person', "record.emailAddress = 'norm.network@mycompany.com'")

Here's another example.  Let's automatically set the priority to High if the summary or description contains the word "urgent", "emergency", or "important", or something similar such as "urgently".

Condition: $old.id = null and fullTextSearch($summary + ' ' + $description, "urgent~ important~ emergency~")

Date: empty (now)

Action: Assignment.  Field: 'priority'.  Value : 'High'

More on what each field and expression means next time.

Dan 

 

Posted on Friday, November 9, 2007 at 11:19AM by Registered CommenterDan Hardy in | CommentsPost a Comment
Page | 1 | 2 | Next 5 Entries