Custom Excel Exports using templates #597447/v7523 |
Tags:
Excel
Table of Contents
Features of the customizable, template based Excel exportGetting started: Exporting using customized Excel templatesThe customized Excel exports are available on the "Export To Office" menu on tracker pages and also on Document-View page. After the "Export To Office" dialog opens you can select an "Excel export" option and use customized Excel exports from there.
You have two options, either:
By clicking on the "Upload Office Template" link you can choose the template .xls or .xlsx file from the local file system, and once it is uploaded that file will be used as template. Alternatively you can drag-and-drop files to this link from file-systems if you are using a modern browser (mostly exception is the Internet Explorer versions). Managing Excel templatesOnce your Excel template is complete you can finally upload that to the Document Management, so other users can easily find and use them.The place/directory where these templates are stored is accessible by clicking on the "Manage custom templates" link on this dialog. Any .xls or .xlsx files which are uploaded to this directory will appear in the drop-down list next to the "Use template" text, and can be easily selected from there. Writing Excel templatesWhat are the Excel templates?Excel templates are just ordinary and simple .xls or .xlsx files which contain certain scripts and special tags written into the cells of the template file. These scripts can control what content is exported and how it will look like. During exporting of a template the scripts are evaluated for the input codeBeamer entities (the bugs/issues/tasks/requirements from the tracker) and the result is put into the result Excel document. Those parts of the original template are preserved - like charts a kept- as is which does not contain these special scripts. The Excel export is using a simple yet powerful open-source templating engine JETT Simple properties exportTo create a simple export template you can start with a new clean Excel sheet, and put scripts into cells of the template. For example the following Excel sheet will export the items (bugs, requirements etc)'s Status and Description to Excel:
What is happening here is that during the export the CodeBeamer will search the template Excel document and finds the the scripts by looking at the cell's content. If a script like ${items.description} is found then this script is evaluated for each exported items (TrackerItemDtos) and the result of this evaluation is put into the export document. If the script is using the "items" variable the script that means that the script is executed once for every exported item, and there is automatically a new row created in the result document. Special "items" variable: Implicit collection processingWhen using the "items" variable in a script that automatically recognized, and that single row which contains the ${items.description} is evaluated for every issues. The result then will be added as one new row below each other. This two screenshot shows the template and the result:
Exporting issue propertiesYou can access the issue's properties using a simple expression language which uses the common $ and "dot" notation which is used in many scripting languages. For example the ${items.description} is used to access the item.getDescription() java "getter". The export engine uses the JEXL expressions There are more information available here about these scripts: JEXL syntax Accessing custom or customized fields by their labelsSome fields like the "name", "id" and "description" are always available in all trackers, so it is very easy to their values by simply using property access like ${item.description}.Custom fields are however a more difficult to access. Let's have a look at this tracker definition:
There is a "Module" custom field defined in this tracker, which is stored in the "customField(0)" property (visible if you turn on "Show property name" checkbox above). To get the "Module value you would have to use the ${item.getCustomField(0)} expression in Excel templates, which is quite inconvenient. Also that would limit the reusability of your template in case the "Module" fields is stored in different physical properties in different trackers. So for this reason it is better to access such fields by using their label as expression. So use this expression: ${fields.getByLabel(items,"Module")} This will find the field with label "Module" on item, and return the field's value. Much better for template reuse! Choice Option FieldsChoice option fields can be accessed through getChoiceList(<index>) method. These are collections, therefore their values are retrieved by querying collection items. For single option choice fields the item with 0 index has to be retrieved. For example getting the value of a choice field with property name choiceList[1] would be: ${items.getChoiceList(1).get(0).getName()} Accessing fields by label is also possible: ${fields.getByLabel(items,"<field label>").get(0).name} Please see attached an example template for exporting user added fields. This example is for a standard Bugs tracker with the following added fields:
The template file contains two sheets. One for accessing the fields by property names, and a second one for getting the fields by labels. Multichoice FieldsIf a field is configured for multiple choice options (see screenshot below), and can have more values, then its items have to be iterated through as described in section
For example: <jt:if test="${item.getChoiceList(1) != null}"><jt:forEach items="${item.getChoiceList(1)}" var="choice"> ${choice.name}</jt:forEach></jt:if> Note the difference in syntax when referring to collection items opposed to complete collections ( e.g. item.getChoiceList(1) vs. items.getChoiceList(1) ). Please see attached an example template for exporting multichoice fields. The caveat of this script above is that it will put each choice value to a new line. If you want the choice values into a single cell and separated by a "," character this small groovy script with our groovy tag can do that. This script will print out the "Category" field's values to a cell. <cb:groovy template="false" silent="false">cats = fields.getByLabel(item,"Category"); result = []; cats.each({result.add(it.name)}); return result.join(",");</cb:groovy> Available variables in scripts (scope)Following objects are accesssible in the scripts:
For more information about the java objects and their properties please check the javadoc shipped with your codeBeamer version or check the latest javadoc online Explicit processing of collections and content filteringFor many simple cases it is easy to create export rows using the "items" variable and implicit processing as seen before. However in many cases you may want to filter the output data and only export just a part of the issues in a tracker. For example if you want to export only some issues which are created "today or yesterday" then you should
This is what is put to the template:
Here the jt:forEach tag will iterate over all issues in the "items" collection (which is the exported issues) and then repeat all cells between the start and end of the jt:forEach tag. The current issue will be exposed as "item" variable, so the cells inside the jt:forEach should refer to issue properties using that. Then the jt:if tag is used to filter which item is added using a simple logic expression. The item.submittedAt contains the submit date of the issue, and the "yesterday" is a date constant always refers to the start (midnight) of yesterday. As expected the elements inside the jt:if tag will appear only if the condition is true. Tags availableThere are many more tags and more tag options available than the jt:forEach or jt:if tag in the previous example. It is important to note that the tags must be closed i.e. if a <jt:forEach> tag is opened there must be a matching </jt:forEach> tag appearing at the end of the block. Some some examples:
For the complete list of available tags you can check the JETT tag documentation Advanced script tags and Groovy/Velocity scriptsWhile simple property acesss and JEXL expressions For these cases the Excel export contains few tags that can execute a Groovy These tags are available:
For example this tag: <cb:groovy>Groovy name:${item.name}</cb:groovy> Will set the cell to if the exported item's name is "my bug" Groovy name:my bug Another example for using a Regexp <cb:groovy template="false" silent="false"> email = item.submitter.email; domainRegexp = /@(.*?)\./; matcher= (email =~ domainRegexp); return matcher[0][1];</cb:groovy> This will extract domain name from the email address of the issues. For example if the submitter is "zluspai@intland.com" the script will put the "intland" text to the cell. Limitations of scripts:
The cb:groovy tag has some special feature that it can run in two different modes: as template or as script. The "template" mode means that the script is evaluated as a Groovy Simple Template
An example of this: <cb:groovy>Hello ${world}</cb:groovy> On the other hand, the Groovy template can run in "script" mode. That mean that the script-text is evaluated as some Groovy code. For example: <cb:groovy template="false" silent="false">item.submittedAt</cb:groovy> This differs from the template mode as:
Auto-sizing columnsThe cb:autosize advanced tag will auto-size the columns to fit their content automatically. Just put the starting <cb:autosize> tag to the 1st column and the closing </cb:autosize> tag to the last column and the result Excel sheet will be nicely sized for those columns.
Advanced functions and expressionsBesides the accessing bean's properties the expressions can also access static java methods directly. Just use the fully qualified class name as prefix, for example this calls a static method an String object: ${java.lang.String.format('Hello %s', 'world')} The output is the obvious "Hello world". The other advanced function is that you can access the static methods on Apache Commons' StringUtils ${StringUtils:substringAfter(item.name, "-")} Filtering by datesFor easier filtering issues by dates the render variables contain the following date constants automatically. These are mostly self explanatory, the "begin" means the 1st second of the mentioned date/range, the "end..." means the "last..." second of that:today tomorrow yesterday thisWeek thisMonth lastMonth thisYear thisQuarter previousQuarter beginOfToday endOfToday beginOfTomorrow endOfTomorrow beginOfYesterDay endOfYesterDay beginOfThisWeek endOfThisWeek beginOfNextWeek endOfNextWeek beginOfLastWeek endOfLastWeek beginOfThisMonth endOfThisMonth beginOfNextMonth endOfNextMonth beginOfLastMonth endOfLastMonth beginOfThisYear endOfThisYear beginOfNextYear endOfNextYear beginOfLastYear endOfLastYear beginOfQuarter endOfQuarter beginOfPreviousQuarter endOfPreviousQuarter You can also reference to certain dates using a text description of that and convert it to real Date using the dates.get("...expression...") function. For example followings are valid: //same as beginOfQuarter: dates.get("Start of this quarter") // same as endOfQuarter dates.get("End of this quarter") To get a specific date use this method, which returns 1st of September in 2014: dates.get(2014, 9, 1) To get a relative date from today use past() function,for example to get the day 180 days ago: dates.past(180) To create a date using the current user's default date or datetime format use: // parses date or datetime dates.parse("Sep 01 2014") // parses only date, ignores time dates.parseDate("Sep 01 2014") // parses a date with time dates.parseDateTime("Sep 01 12:34 2014") Example: rendering data in multi-rowCreating an export where an issue appears in multiple row instead of a single row is easy, just use a jt:forEach tag which spans multiple rows. All those rows will be repeated and the expressions inside are evaluated.The template is:
And the output is:
Note: if you want a more compact look: to remove the blank line between block the jt:forEach should be put to just before the "Name" text in the same cell. Hyperlinks back to codeBeamerIf you want to render a hyperlink that points back to codeBeamer you should use this: <jt:hyperlink address="${cbBaseUrl}${item.urlLink}" value="${item.id}"></jt:hyperlink> As result the item's "id" will become a hyperlink which can be clicked on inside Excel, and it will open the same issue in a new browser window. The hyperlink tag does not support implicit iteration: it wont work with the items variable. So this won't work:<jt:hyperlink address="${cbBaseUrl}${items.urlLink}" value="${items.id}"></jt:hyperlink> Instead of you have to do an forEach iteration
Executing SQL queriesEven if it is advised to query data using the DAO and Manager objects, but sometimes one just can not avoid having a nice little SQL in templates.By using the "jdbc" object you can execute SQL queries as this example shows:
This little fragment will export users' names and emails from codeBeamer to Excel. Adding ChartsYou can also include charts to the Excel templates such way that charts will automatically pick up the exported data and will nicely present that. So let's create charts using following steps ! Export source data for chartsFirst of all you should prepare the data which will be visualised in the chart. That is performed the usual way by referring properties of items, or using tags or any similar way. There is one requirement for the data export however: in our example we are defining a pivot chart using named ranges to feed the charts with data. This means that the export data must have a header line too as highlighted in the following screenshot.
Put sample data into the Excel template for chartsAs soon as the data is prepared we can start defining the charts. Yet before we could define the chart there must be some sample data available in the Excel sheet already. Such sample data contains a subset of the real export data, and required because Excel does not allow defining a chart without providing a some data to it. The best practice is that you do an export now with the scripts you've defined in the previous step. Then pick a part of the export data and copy into the Excel template. Important: the sample data is not needed in the final Excel export and therefore should be removed from the final result. In order to remove that you should wrap the sample data in a <jt:if test='false'> condition, like this: |<jt:if test="false">|...sameple data|</jt:if> A screenshot as illustration:
Create named ranges on sample dataAfter the sample data is included you should define a named range So for defining a named range go to:
IMPORTANT: your named range should also contain the static header cell above the range. As in this screenshot it should contain the "Module" text too!
Define charts using named rangesNow as the named ranges is defined on the sample data we can create a chart using that. Let's insert an Pivot chart and Pivot table:
Now the Pivot table and chart is added, yet it is empty. To select the data it will display use the Pivot-Table sidebar:
Finally an important step: we should tell the exporter to ignore the charts and the data in the pivot tables, even if they contain some scripts. For that add a <jt:null> tag around the chart so that part won't be touched. An example: |<jt:null>|...chart...| |...|...chart...| |...|...chart...|</jt:null> Now the Pivot Table and Pivot Chart is ready, both is showing the sample data and referencing the sample data being exported. Re-bind named ranges to the real data being exportedThe last step missing is that we need to redefine the named range during the Export to point to the real data. This is done by adding a special <jt:name> tag which defines the new named range. For example the "module" named-range will be generated from the scripts in the $H1$1:$H$2 range, so that is rebound by using this tag: <jt:name name="module" formula="$[$H$1:$H$2]" />
Exporting charts and and example templateFinally once the template with charts is ready the export should:
You can find the attached Template-with-charts.xlsx as useful example for defining exports with charts. That demonstrates:
Tips and tricksAvoid Excel cell limits: cut long contentExcel has certain limitations of the content![]() In order to fix this the description should be abbreviated during export. Just use this expression: ${StringUtils:abbreviate(items.description, 1000)} If this limit is exceeded Excel will report an "Repaired Records: String properties from /xl/sharedStrings.xml part (Strings)" error when opening the result Excel file, so this is is an indication of such problem. Using Regular ExpressionsRegular expressions (Regexp![]() zluspai@javaforge.com zoltan.luspai@intland.com And want to get the domain name in a cell like "javaforge" or "intland" should be extracted, so a report can be made which tells how many bug reports there grouped by the companies. So the result should be: javaforge intland So then you can use Regexp and the next groovy script, which extract the "domain" part: <cb:groovy template="false" silent="false"> email = item.submitter.email; domainRegexp = /@(.*?)\./; matcher= (email =~ domainRegexp); return matcher[0][1];</cb:groovy> This works fine, yet a bit complex. An alternative is to use a "Regexp:findFirst()" custom function which can extract the first match of a Regexp. This produces the same result: ${Regexp:findFirst(item.submitter.email, "@(.*?)\.", "$1")} The Regexp:findFirst() function will accept these parameters:
Additionally the "Regexp:findAll()" function is available which works similar to the "findFirst" method, yet scans for all matches, and puts the results to a List (of Strings). |
Fast Links
![]() codebeamer Overview codebeamer Knowledge Base Services by Intland Software |
This website stores cookies on your computer. These cookies are used to improve your browsing experience, constantly optimize the functionality and content of our website, and help us understand your interests and provide more personalized services to you, both on this website and through other media. With your permission, we and our partners may use precise geolocation data and identification through device scanning. You may click accept to consent to our and our partners’ processing as described above. Please be aware that some processing of your personal data may not require your consent, but you have a right to object to such processing. By using our website, you acknowledge this notice of our cookie practices. By accepting and continuing to browse this site, you agree to this use. Your preferences will apply to this website only.
Note that user-behavior analytics are being captured on this server to improve the Codebeamer user experience.