1.1Calculated Issue Fields
From CB-5.4 you can define read-only tracker fields whose content is calculated from other tracker item fields.
Every custom field can become a computed field, by specifying an expression or formula for the field:
Figure: Tracker Customize Field Properties
1.1.1.1Example 1: Past Deadline Test (for Issue Resolution )
In this example, we want to display whether we have already passed an issue's scheduled End Date. Click 'Add custom field' at the bottom of the Tracker → Customize → Field Properties tab. ('Past End Date' is already implemented by codeBeamer, using the red exclamation mark in the issue display). Our new custom field is called 'Past Deadline', and it must be set up as type boolean, and it returns true if the issue is very late. Under the 'Layout and Content' column of the Field Properties, for the new custom field, the '=' field is defined as:
not(endDate >= fn:Date("today"))
Be sure to checkmark the list column to list the field (Selected in the Field Properties) if desired. Also be sure to set up appropriate permissions under the Field Access tab, to allow the field to be viewed. The write permissions, as specified under Field Access, only take effect if the custom field's computed-value field definition is deleted, and it becomes a non-computed field. For more information about functions available for the computed tracker fields, see the Functions section of this document, below.
1.1.1.2Example 2: Weight = Priority * Severity
We want to combine issue priority and severity into a new field “Weight”, with
Weight = Priority * Severity
Priority is a (single) choice field with the following choice values:
Figure: Priority Choice Field Values
Severity is a (multiple) choice field with these defined values:
Figure: Severity Choice Field Values
All choice fields, except Status and Priority, are lists or arrays of values. However, the GUI for static choice fields currently only allows selection of a single value. So in order to access the first/single value of a (multiple) choice field, we must use the [0] operator, e.g. Severity[0].
The “Highest” priority and the highest severity (“Blocker”) selections both have the lowest ID, so in order to compute a Weight proportional to the logical order of Priority and Severity, we must use operands that are inversely proportional to the choice value ids, e.g. : integer Weight =
(5 - Priority.id) * (6 - Severity[0].id)
An empty Priority or Severity would yield an id of null, so the above formula would return the highest possible weight (= 30) for issues with empty Priority and Severity, which is wrong, so we must handle empty values appropriately, i.e.: integer Weight =
(empty Priority ? 0 : 5 - Priority.id) * (empty Severity ? 0 : 6 - Severity[0].id)
1.1.1.3Example 3: Compute column(s) in an embedded table from the values of other column(s)
Since CB-7.3, a tracker (item) can also have tables. A table consists of one or more columns.
E.g. A table called Matrix containing four columns:
![[!NumberMatrix.png!]](/cb/displayDocument/NumberMatrix.png?doc_id=13888473&version=1&history=false¬ification=false)
The first two columns A and B contain numeric operands and the other two columns should contain the product and sum/total of the operands (per row).
If you are addressing a table column, e.g. via it's attribute/property name tableColumn[0,1], then you must be aware, that you always address the whole column (an array/vector of column values (indexed by row)).
E.g. for this table
The expression A or tableColumn[0,0] would yield [1, 2, 3] and B or tableColumn[0,1] would be [5, 6, 7].
To address the whole table, use the table's name or attribute, e.g. Matrix or table[0], which would yield an array of table rows, where each row is an array of table column values (in the order of the columns).
[
[1, 5],
[2, 6],
[3, 7]
]
The first table row can be accessed as Matrix[0] or table[0][0], and would yield [1, 5].
So how can we define a third column Product, whose value is the product of A multiplied by B (for each row) ?
If we would define Product to be computed as
tableColumn[0,1] * tableColumn[0,2]
this would mean a multiplication of two vectors, e.g. [1, 2, 3] * [5, 6, 7], an operation not supported by the expression language. Also the result would be a two dimensional array, and not a one-dimensional column value vector !
The solution is, to use a projection: table.{ row | expression } , that iterates over each row in the table and produces an array of values, one value per row according to expression!
![[!image-15fc04252ce.png!]](/cb/displayDocument/image-15fc04252ce.png?doc_id=3304528&version=1&history=false¬ification=false)
The projection/expression
table[0].{row | row[0] * row[1] }
reads as:
- Iterate over table[0]
- for each row (an array of column values)
- evalute the expression row[0] * row[1]
- return an array of the expression values per row
So for our example table, the result would be the array/vector: [5, 12, 21]
Please note that tableColumn[x,y] is a fixed property name text, therefore y is not adjusted when column position is changed. When defining formulas involving table columns, one may not rely on the order of columns, but property names have to be checked (this can be done by setting the "Show property name" check-box in field configuration page).
At other places [y] is used as an index number (not text). For example table[x][y] refers to the y-th row in table[x].
1.1.2Syntax
Unified Expression Language (EL) syntax is used for the field values' formulae.
The unified expression language defines the following literals:
- Boolean: true and false
- Integer: as in Java
- Floating point: as in Java
- String: with single and double quotes; " is escaped as \", ' is escaped as \', and \ is escaped as \\ .
- Null: null
In addition to the . and [] operators, the expression language provides the following operators:
- Arithmetic: +, - (binary), *, / and div, % and mod, - (unary)
- Logical: and, &&, or,||, not, !
- Relational: ==, eq, !=, ne, <, lt, >, gt, <=, ge, >=, le. Comparisons can be made against other values, or against boolean, string, integer, or floating point literals.
- Empty: The empty operator is a prefix operation that can be used to determine whether a value is null or empty.
- Conditional: A ? B : C. Evaluate B or C, depending on the result of the evaluation of A.
- Projection: .{alias|expression} Is a special operator on collections, that iterates over the collection and creates a new collection by evaluating the specified (sub-) expression for each element alias of the original collection.
The precedence of operators highest to lowest, left to right is as follows:
- [] .
- () - Used to change the precedence of operators.
- - (unary) not ! empty
- * / div % mod
- + - (binary)
- < > <= >= lt gt le ge
- == != eq ne
- && and
- || or
- ? :
The following words are reserved for the JSP expression language and should not be used as identifiers.
and |
eq |
gt |
true |
instanceof |
|
or |
ne |
le |
false |
empty |
|
not |
lt |
ge |
null |
div |
mod |
Many of these words are not in the language now, but they may be in the future, so you should avoid using them.
For more information on the Unified Expression Language, you could start with: http://java.sun.com/javaee/5/docs/tutorial/doc/bnahq.html
1.1.3Context variables
The execution/evaluation context of a unified expression contains the following objects:
Object |
Type |
Comment |
user |
UserDto |
The current user, that executes/evaluates the expression (since CB-9.2) |
project |
ProjectDto |
The current project, the expression is evaluated in |
tracker |
TrackerDto |
The current tracker, the expression is evaluated in |
this |
TrackerItemDto |
The current tracker item, the expression is evaluated on |
You can use the dot (
.) operator, to access attributes of context objects, e.g.
user.realName or
project.name.
To access attributes (fields) of the current tracker item (
this), you can ommit
this. in front of the attribute/field identifier, e.g.
submitter is equivalent to
this.submitter.
To refer to (the value) of tracker item fields, the following field identifiers are allowed:
- the field property/attribute name, e.g. id, name, description, submitter, submittedAt, namedPriority, status, assignedTo, subjects, categories, versions, customField[0], choiceList[2], etc.
- or the field label, but only,
- if the label only consists of alphanumeric characters ([A-Z],[a-z],[0-9]), ‘$‘(dollar sign) and ‘_‘ (underscore),
- does not start with digits ([0-9])
- and does not contain HTML markup.
- e.g. Color is allowed, but Background color, Best.-Nr., Estimated<br/>Effort or <b>v</b><sub>max</sub> (vmax) are not allowed
- or the RESTful field name (lower camel case
of field label, with all HMTL markup and not allowed characters removed)
- e.g. color, backgroundColor, bestNr, estimatedEffort and vmax
The field's property and label are displayed in their respective columns in the Tracker → Customize → Field Properties area.
To access individual values of multi-value choice fields, use the
[] operator, e.g.
categories[0], choiceList[2][0]
To access attributes of complex field values, e.g. choice values, use the
. operator followed by the attribute, e.g.
Priority.id, Status.name, Resolution[0].name
All references, choice and members field values have at least the following attributes:
To extract attributes from a multi-value field, use a projection. E.g. to create a list of the names of all users/roles assigned to the item, you can use:
assignedTo.{member|member.name}
You can use such a projection in combination with the function valuesInList to check if the item is assigned to some particular users, e.g.
fn:valuesInList(assignedTo.{member|member.name}, “bond”, “Project Admin”) ?
1.1.4Functions
Within field value expressions/formulas, you can use the following standard functions:
Function |
Signature |
Meaning |
concat |
String concat(Object...) |
Converts all parameters to strings and concatenates them to a single result string. |
contains |
boolean contains(String text, String part) |
Tests, if the specified text contains the specified part |
containsIgnoreCase |
boolean containsIgnoreCase(String text, String part) |
Tests, if the specified text contains the specified part, irrespective of case |
endsWith |
boolean endsWith(String text, String suffix) |
Tests if the specified text ends with the specified suffix |
escapeXml |
String escapeXml(String text) |
Escapes characters with a special meaning in XML with appropriate escape sequences |
format |
String format(String pattern, Object... args) |
Create a string representation of the specified arguments using a MessageFormat pattern. |
indexOf |
int indexOf(String text, String part) |
Finds the first index of the specified part within text, or -1 if not found |
join |
String join(String[] array, String separator) |
Joins the elements of the provided array into a single String, separated by the separator |
length |
int length(Object) |
Returns the length of the passed string, or the number of elements of the passed array or collection |
printf |
String printf(String format, Object... args) |
Create a string representation of the specified arguments using a printf like format |
replace |
String replace(String text, String replace, String replacement) |
Replaces a String with another String inside a larger String |
split |
String[] split(String text, String separator) |
Splits the provided text into an array (at each occurence of the separator) |
startsWith |
boolean startsWith(String text, String prefix) |
Tests if the specified text starts with the specified prefix |
substring |
String substring(String text, int start, int end) |
Gets a substring from the specified String, from the start position (inclusive) to the end position (exlusive) |
substringAfter |
String substringAfter(String text, String separator) |
Gets the substring after the first occurrence of the separator |
substringBefore |
String substringBefore(String text, String separator) |
Gets the substring before the first occurrence of the separator |
substringBetween |
String substringBetween(String text, String open, String close) |
Gets the substring after the first occurrence of the open string and before the occurance of the close string |
toLowerCase |
String toLowerCase(String text) |
Converts a String to lower case |
toUpperCase |
String toUpperCase(String text) |
Converts a String to upper case |
trim |
String trim(String text) |
Removes leading and trailing whitespace |
The case of function names is significant. The usage of the prefix fn: when referring to functions is optional, e.g.
concat(customField[3], " ", customField[4], " has downloaded and installed codeBeamer.")
or
fn:format("{0,date,yyyy-MM-dd HH:mm}", submittedAt)
In CodeBeamer 8.2.1 and newer, there is also:
Function |
Signature |
Meaning |
distinct |
List distinct(List list) |
Returns a list with all not null/distinct/unique values in the specified list. |
min |
Object min(List list) |
Returns the minimum/smallest value in the specified list |
max |
Object max(List list) |
Returns the maximum/largest value in the specified list |
sum |
Number sum(List list) |
Returns the sum/total of all numeric values in the specified list |
avg |
Double avg(List list) |
Returns the average of all numeric values in the specified list |
In CodeBeamer 9.3.0 and newer, there is also:
Function |
Signature |
Meaning |
ascending |
List ascending(List list) |
Returns a new list, that contains the items of the specified list in ascending order |
descending |
List descending(List list) |
Returns a new list, that contains the items of the specified list in descending order |
reverse |
List reverse(List list) |
Returns a new list, that contains the items of the specified list in reverse order |
first |
List first(int x, List items) |
Returns a new list, that contains the first x items of the specified list, or the list itself, if it does not contain more than x items |
last |
List last(int x, List items) |
Returns a new list, that contains the last x items of the specified list, or the list itself, if it does not contain more than x items |
These new functions (plus the previously existing length and join allow to define map/reduce expressions, typically but not exclusively, in conjunction with (multi-level) projections.
E.g.:
A task has multiple subjects (via the subjects reference field). Each subject is another tracker item, that each has a priority.
The priority of the task should reflect the highest priority of all subjects:
max(subjects.{subject|subject.priority})
E.g.:
A Bug refers to one or more Builds, that are affected by this bug (via a custom reference field, e.g. choiceList[1]).
Each Build is a config item, that itself refers to a Release (via the versions field). Different Builds can belong to the same Release.
The field Detected in (Release) of the Bug should now be automatically computed to reflect the Releases of all Builds affected by this bug:
distinct(choiceList[1].{build|build.versions.{version|version}})
Please note the nested projections, which executes as
List result;
for (Object build in choiceList[1]) {
for (Object version in build.versions) {
result.add(version);
}
}
return distinct(result);
E.g.:
The Severity of an item should be the highest Severity of all Subjects:
List(min(subjects.{subject|subject.severities.{severity|severity}}))
or
first(1, ascending(subjects.{subject|subject.severities.{severity|severity}}))
Please note:
- Severity is a choice list/field, so the field value is a list of choice options. By default, only one list element is allowed.
- Severity choice options are ordered descending from Highest to Lowest, so we must use min or first in order to get the highest severity.
E.g.:
Show the last two comments on the tracker item:
join(first(2, reverse(distinct(attachments.{attachment|attachment.description}))).toArray(), "\\\\")
That reads as:
CodeBeamer also provides the following specific functions:
Function |
Signature |
Meaning |
List |
List List(Object...) |
Create a List from the passed arguments or the passed array |
Date |
Date Date(String date) |
Create a date according to the passed argument |
roundDate |
Date roundDate(Date date, String precision) |
Round a date according to the specified precision (see below) |
truncateDate |
Date truncateDate(Date date, String precision) |
Truncate a date according to the specified precision (see below) |
shiftDate |
Date shiftDate(Date date, int distance, String unit) |
Shift a date by the specified distance in the specified unit (see below) |
isSameDay |
boolean isSameDay(Date date1, Date date2) |
Returns true if both dates are within the same calendar day. |
round |
Integer round(Number number) |
Rounds the specified (floating point) number to the nearest Integer. |
countryCode |
String countryCode(String country) |
Get the ISO 3166 2-letter country code for the specified country code or English country name |
valueInList |
boolean valueInList(Object value, Object... values) |
Returns true if the first argument is equal to any of the following arguments |
valuesInList |
boolean valuesInList(Iterable list, Object... values) |
Returns true if any element of the specified collection or array is equal to any of the following arguments. |
objectInList |
boolean objectInList(NamedDto object, String csv) |
Returns true if the specified object is referenced in the specified comma-separated list of object references |
objectsInList |
boolean objectsInList(List<? extends NamedDto> objects, String csv) |
Returns true if any of the specified objects is referenced in the specified comma-separated list of object references |
objectIdInList |
boolean objectIdInList(Identifiable object, Integer... ids) |
Returns true if the ID of the specified object is equal to any of the specified ids |
objectIdsInList |
boolean objectIdsInList(List<Identifiable> objects, Integer... ids) |
Returns true if the ID of an object in the specified list is equal to any of the specified ids. |
In CB-9.2 and newer, there is also:
Function |
Signature |
Meaning |
userInGroup |
boolean userInGroup(UserDto user, String... groups) |
returns true, if the specified user is member in at least one of the specified user groups, otherwise false |
userInRole |
boolean userInRole(UserDto user, ProjectDto project, String... roles) |
returns true, if the specified user has at least one of the specified roles in the specified project, otherwise false |
userHasPermission |
boolean userHasPermission(UserDto user, String... permissions) |
returns true, if the specified user has at least one of the specified System permissions (see table below), otherwise false |
userHasProjectPermission |
boolean userHasProjectPermission(UserDto user, ProjectDto project, String... permissions) |
returns true, if the specified user has at least one of the specified Project permissions (see table below) on the specified project, otherwise false |
userHasTrackerPermission |
boolean userHasTrackerPermission(UserDto user, TrackerDto tracker, String... permissions) |
returns true, if the specified user has at least one of the specified Tracker permissions (see table below) on the specified tracker, otherwise false |
The parameter for the function
Date, can be either a date specified as:
or one of the following String constants:
- "Today"
- "Tomorrow"
- "Yesterday"
- "Start of this week"
- "End of this week"
- "Start of next week"
- "End of next week"
- "Start of last week"
- "End of last week"
- "Start of this month"
- "End of this month"
- "Start of next month"
- "End of next month"
- "Start of last month"
- "End of last month"
- "Start of this year"
- "End of this year"
- "Start of next year"
- "End of next year"
- "Start of last year"
- "End of last year"
E.g:
endDate >= Date("today")
The precision for roundDate and truncateDate and the unit of shiftDate must be (an abbreviation of) :
- "Year"
- "Month"
- "Week"
- "day"
- "hour"
- "minute"
- "second"
E.g:
roundDate(endDate, "h")
shiftDate(startDate, 30, "min")
For single value attributes like status, priority or submitter, you can use valueInList().
E.g. Check if the name of the user that has submitted the item is “bond” or “klaus”:
valueInList(submitter.name, "bond", "klaus")
But for attributes with multiple values (e.g. assignedTo, supervisors, subjects, versions, etc. plus all custom choice lists), checking for constant values requires valuesInList() in combination with a projection.
E.g. To check if the issue owner/supervisors list contains any of the specified users (by name):
valuesInList(supervisors.{principal|principal.name}, "bond", "klaus")
The function objectIdsInList() is functionally equivalent to
valuesInList(objects.{object|object.id}, ids)
E.g: Check if the field “Resolution” contains a value with ID 2 or 4:
objectIdsInList(resolutions, 2, 4)
or
valuesInList(resolutions.{resolution|resolution.id}, 2, 4)
The groups parameter(s) for the function userInGroup, are the names of User Groups:
User Group |
Description |
sysadmin |
The System Administrators Group |
user |
The "Regular" User Group |
There can be any number of additional user groups.
In most cases, it will be more appropriate to check, if a user has a specific
System Permission (that can be granted via different
User Groups), e.g. instead of
userInGroup(user, "sysadmin")
you would use
userHasPermission(user, "system_admin")
The roles parameter(s) for the function userInRole, are the names of Project Roles :
Project Role |
Description |
Project Admin |
The role for Project Administrators |
Developer |
The role for (Software) Developers in the project |
Stakeholder |
The role for Stakeholders in the project |
Product Owner |
The role for Product Owners in the project |
Scrum Master |
The role for Scrum Masters in the project |
Test Lead |
The role for Test Lead(er)s in the project |
Test Engineer |
The role for Test Engineers in the project |
Tester |
The role for Testers in the project |
There can be any number of additional project roles.
E.g. Check if the current user has the role
Developer or
Tester in the current project:
userInRole(user, project, "Developer", "Tester")
P.S. In most cases, it will be more appropriate to check, if a user has a specific
Project Permission (that can be granted via different
Project Roles), e.g. instead of
userInRole(user, project, "Project Admin")
you would use
userHasProjectPermission(user, project, "project_admin")
The permissions parameter(s) for the function
userHasPermission, are the names of
System Permissions:
System Permission |
Description |
wiki_edit_own_page |
Allows users to edit own Wiki pages |
account_admin_own |
Allows users to administer own account data settings |
account_modify_own_timezone_dateformat |
Allows users to administer own timezone and date format data settings |
account_admin |
Allows users to administer all account data settings (name,e-mail,password, phone number, ...) |
account_address_view |
Allows users to view the address field in all accounts |
account_company_view |
Allows users to view the company field in all accounts |
account_phone_view |
Allows users to view the phone field in all accounts |
account_email_view |
Allows users to view the email field in all accounts |
account_skills_view |
Allows users to view the skill field in all accounts |
account_role_view |
Allows users to view user group settings and members |
account_role_admin |
Allows users to create, administer user groups and assign members |
document_add_global |
Allows users to add new project-independent documents |
label_public_create |
Allows users to create new public tags |
label_public_admin |
Allows users to administer public tags |
system_project_create |
Allows users to create a new project |
system_admin |
Allows users to administer the portal |
service_desk |
Allows users to access the Service Desk |
queries_view |
Allows users to access Queries |
review |
Allows users to do Reviews |
api_permission |
Allows user to access to the Rest / Remote API |
only_api_permission |
If set, users in groups with this permission cannot use the GUI |
System permissions are granted to user groups and indirectly to all users in that groups.
The permissions parameter(s) for the function
userHasProjectPermission, are the names of
Project Permissions:
Project Permission |
Description |
wiki_space_view |
Allows users access to the project Wiki |
document_view |
Allows users access to the project "Documents" |
document_view_history |
Allows users to view the document version history |
document_add |
Allows users to add new documents to a project |
document_unpack |
Allows users to upload and unpack Zip and Tar files |
document_subscribe |
Allows users to subscribe documents to get email notifications when a document is read or modified by an other user |
document_subscribe_others |
Allows users to subscribe documents for project members to get email notifications when a document is read or modified by an other user |
document_subscribers_view |
Allows users to view the subscriber list |
tracker_view |
Allows users to view tracker list and the "Trackers" tab |
tracker_admin |
Allows users to administer project trackers, set permissions, add custom fields, set default fields, ... |
tracker_report |
Allows users to access and execute reports |
cmdb_view |
Allows users to view the project CMDB |
cmdb_admin |
Allows users to administer the project CMDB, create categories, set permissions, add custom fields, set default fields, .. |
branch_view |
Allows users to view branches |
branch_admin |
Allows users to administer tracker branches |
baseline_view |
Allows users to view baselines |
baseline_admin |
Allows users to administer project baselines |
scm_view |
Allows users to view the repository hierarchy in this project, even if they don't have access to any of the repositories. |
scm_admin |
Allows users to create new top-level repositories in this project, and to update or delete any existing one. |
members_view |
Allows users to view project members |
members_admin |
Allows users to administer project members |
member_role_view |
Allows users to view project role permission settings |
project_admin |
Allows users to administer all project settings |
Project permissions are granted to project roles and indirectly to all project members with that roles.
The permissions parameter(s) for the function
userHasTrackerPermission, are the names of
Tracker Permissions:
Tracker Permission |
Description |
issue_add |
Allows users to create new items in a tracker |
issue_view |
Allows users to see own items in a tracker |
issue_view_not_own |
Allows users to see all items in a tracker |
issue_edit |
Allows users to edit own items in a tracker |
issue_edit_not_own |
Allows users to edit any items in a tracker |
issue_mass_edit |
Allows users to mass edit multiple items in a tracker |
issue_close |
Allows users to close items in a tracker |
issue_delete |
Allows users to delete items in a tracker |
issue_history_view |
Allows users to see the history of items in a tracker |
issue_escalation_view |
Allows users to view issue escalation schedules in a tracker |
issue_attachment_view |
Allows users to view issue comments or attachments in a tracker |
issue_comment_add |
Allows users to add comments to issues in a tracker |
issue_attachment_add |
Allows users to add attachments to issues in a tracker |
issue_attachment_edit |
Allows users to edit/delete any issue comments or attachments in a tracker |
issue_attachment_edit_own |
Allows users to edit/delete own issue comments or attachments in a tracker |
issue_subscribe |
Allows users to subscribe notifications on a single tracker item |
issue_subscribe_others |
Allows users to manage subscriptions of other users for notifications on a single tracker item |
tracker_subscribe |
Allows users to subscribe notifications on the whole tracker |
tracker_subscribe_others |
Allows users to manage subscriptions of other users for notifications on the whole tracker |
tracker_subscribers_view |
Allows users to view subscriptions of other users for notifications on the whole tracker |
admin_public_view |
Allows users to administrate (create, update and delete) public views on the tracker |
issue_association_view |
Allows users to see tracker item associations |
issue_association_edit |
Allows users to edit (create, update and delete) tracker item associations |
issue_suspected_merge |
Allows to merge tracker item |
branch_merge |
Allows to merge branches |
Tracker permissions are granted to project roles via Tracker → Customize → Permissions, and so indirectly to all project members in that roles.
1.1.5Caveats
If you define computed fields, please beware of the following pitfall:
When displaying computed fields, CodeBeamer always computes the value to display on the fly. But because filtering and sorting of the displayed issues is done at database level (via SQL), codebeamer also stores the (shadow) values of computed fields in the database. These shadow values of computed fields in the database are only updated when issues are created or modified, but not when a tracker field computation formula is defined or modified. If you modifiy a field formula for a tracker with existing items, the displayed field values immediately reflect the new formula, but the (shadow) field values in the database do not, and subsequent filtering/sorting by this computed field produces erratic results.
This can also affect exporting data from codeBeamer with custom
Word or
Excel templates.
- Extracting data from calculated fields with method customField[<index>] returns the shadow value from the database.
- On the other hand, method getByLabel($entity, "Description") returns the value calculated on the fly.