Query Functions¶
Jira-select provides a long list of functions out-of-the-box, and you can add your own if these are not enough.
Jira¶
- get_issue(ticket_number: str) jira.resources.Issue ¶
Fetch a Jira issue by its issue key (e.g.
MYPROJECT-1045
).This will return a
jira.resources.Issue
object; you can access most fields via itsfields
property, eg:get_issue(field_holding_issue_key).fields.summary
- get_issue_snapshot_on_date(issue: jira.resources.Issue) jira_select.types.IssueSnapshot: ¶
Reconstruct the state of an issue at a particular point in time using the issue’s
changelog
.You will want to pass the literal value
issue
as the first parameter of this function. Jira-select provides thejira.resources.Issue
object itself under that name, and this function will use both that object and the changes recorded in thechangelog
field for getting an understanding of what the issue looked liked at a particular point in time.This function requires that you set the query
expand
option such that it includeschangelog
for this to work correctly – if you do not do that, this function will fail.select: snapshot: get_issue_snapshot_on_date(issue, parse_datetime('2022-01-01')) from: issues expand: - changelog
The returned snapshot is not a
jira.resources.Issue
object, but instead ajira_select.types.IssueSnapshot
object due to limitations around what kinds of data can be gathered from the snapshot information. The most important difference between ajira_select.types.IssueSnapshot
and ajira.resources.Issue
object is that thejira_select.types.IssueSnapshot
object is a simpledict[str,str]
object in which the value of thedict
entries is always thestr
-ified field value.
- sprint_name(sprint_blob: str) Optional[str] ¶
Shortcut for returning the name of a sprint via its ID. Equivalent to calling
sprint_details(sprint_blob).name
.
- sprint_details(sprint_blob: str) Optional[jira_select.functions.sprint_details.SprintInfo] ¶
Returns an object representing the passed-in sprint blob.
Jira returns sprint information on an issue via strings looking something like:
com.atlassian.greenhopper.service.sprint.Sprint@14b1c359[id=436,rapidViewId=153,state=CLOSED,name=MySprint,goal=Beep Boop,startDate=2020-03-09T21:53:07.264Z,endDate=2020-03-23T20:53:00.000Z,completeDate=2020-03-23T21:08:29.391Z,sequence=436]
This function will extract the information found in the above string into a
jira_select.functions.sprint_details.SprintInfo
object allowing you to access each of the following properties:id
: Sprint ID numberstate
: Sprint statename
: Sprint namestartDate
: Sprint starting date (as datetime)endDate
: Sprint ending date (as datetime)completeDate
: Sprint completion date (as datetime)
- get_sprint_by_id(id: int) Optional[jira.resources.Sprint] ¶
This function will request the information for the sprint specified by
id
from your Jira server and return it as ajira.resources.Sprint
object.
- get_sprint_by_name(board_name_or_id: Union[str, int], sprint_name: str) Optional[jira.resources.Sprint] ¶
This function will request the information for the sprint matching the specified name and belonging to the specified board. This will be returned as a
jira.resources.Sprint
resource.
- field_by_name(row: Any, display_name: str) Optional[str] ¶
Returns value for field having the specified display name.
Note
You probably do not nee to use this function. We provide another, simpler, method for getting the value of a field by its human-readable name– just place the human-readable name in between curly braces in your query expression; eg:
select Story Points: "{Story Points}" from: issues
Note
You will almost certainly want to provide the parameter named
issue
as the first argument to this function. Jira-select provides the raw row data to functions under this variable name.In Jira, custom fields are usually named something like
customfield_10024
which is, for most people, somewhat difficult to remember. You can use this function to get the field value for a field by its display name instead of its ID.Example:
select - field_by_name(issue, "Story Points") as "Story Points" from: issues
- estimate_to_days(estimate_string: str, day_hour_count=8) Optional[float] ¶
Converts a string estimation (typically stored in
timetracking.originalEstimate
) into an integer count of days.The
timetracking.originalEstimate
field contains values like1d 2h 3m
; using this function will transform such a value into1.25625
.
- flatten_changelog(changelog) List[jira_select.functions.flatten_changelog.ChangelogEntry] ¶
Converts changelog structure from your returned Jira issue into a flattened list of
jira_select.functions.flatten_changelog.ChangelogEntry
instances.Note
You must use the
expand
option ofchangelog
for Jira to return to you changelog information in your query; eg:select: changelog: flatten_changelog(changelog) from: issues expand: - changelog
If you do not provide the necessary
expand
option, this function will raise an error.Every member of the returned list has the following properties:
author
(str): Author of the changecreated
(datetime.datetime): When the change took placeid
(int): The ID of the changelog entryfield
(str): The name of the field that was changedfieldtype
(str): The type of the field that was changedfromValue
(Optional[Any]): The original value of the field. Note that the original Jira field name for this isfrom
.fromString
(Optional[str]): The original value of the field as a string.toValue
(Optional[Any]): The final value of the field. Note that the original Jira field name for this is ``to`.toString
(Optional[str]): The final value of the field as a string.
This returned list of records can be filtered with
simple_filter
to either find particular entries or filter out rows that do not have an entry having particular characteristics.
- get_linked_issue_keys(issue: jira.resources.Issue, link_type: str | None = None) list[str]: ¶
Return a list of issue keys that are related to the relevant issue via the specified relation type (e.g.
causes
,is associated with
, etc.).You will want to pass the literal value
issuelinks
as the first parameter of this function. This will provide this function with the list of issuelinks your issue has.If
link_type
is unspecified, all related issue keys will be returned.For example, to find the keys for all issues that were caused by a particular issue, you could run the following query:
select: caused_bugs: get_linked_issue_keys(issuelinks, 'causes') from: issues where: - type = 'Bug'
Subquery¶
- subquery(subquery_name, **params) Any: ¶
Runs a subquery by name with the provided parameters.
For example: you can get the time intervals during which an issue and its associated subtasks were in the “In Progress” status with a query like so:
select: self_and_child_intervals_in_progress: interval_matching(issue, status="In Progress") | union(subquery("children", key=issue.key)) from: issues subqueries: children: select: in_progress_intervals: interval_matching(issue, status='In Progress') from: issues where: - parent = "{params.key}" expand: - changelog expand: - changelog
Your specified
**params
will become available to the subquery via{params.*}
; in the above example,{params.key}
will be set to the value of the outer query’sissue.key
.Unless specifically specified, a subquery will use the same cache settings as the parent query.
Warning
If you would like your subquery’s cache to be effective, only pass simple values in
**params
.The string representation of an object is used for calculating cache keys, and many objects include information in their default string representations that vary between instantiations. If things like, for example, the memory address of an object appears in its string representation, the cache key will never match, and the cached value will not be used.
A common way that this problem might occur is if you were to pass the entire
issue
object to the subquery. Instead of passing the entireissue
object to the subquery, pass simple values from it as shown in the example above.
Time Analysis¶
- interval_matching(issue, **query_params: dict[str, Any]) portion.Interval ¶
See simple_filter function for information about how to specify
query_params
.Will return an interval covering segments in which the provided issue matches the conditions specified by
query_params
.Note
Contrary to what you might guess, a single portion.Interval object can represnt multiple ranges of time.
Note that portion.Interval objects can be used with logical operations like |, &, and -.
- interval_size(interval: portion.Interval) datetime.timedelta ¶
For a provided interval, return the total amount of time that the interval’s segments span.
- interval_business_hours(min_date: datetime.date | None = None, max_date: datetime.date | None = None, start_hour: int = 9, end_hour: int = 17, timezone_name: str | None = None, work_days: Iterable[int] = (1, 2, 3, 4, 5)) portion.Interval: ¶
Returns an interval having segments that correspond with the “business hours” specified by your paramters.
This is particularly useful when used in conjunction with interval_matching and interval_size above for determining the amount of time an issue was actively in a particular state, for example:
select: total_time_in_progress: interval_size(interval_matching(issue, status="In Progress") & interval_business_hours(parse_date(created))) from: issues
This will find all segments of time during which the relevant issue was in the “In Progress” status during business hours, then return the amount of time that those segments spanned.
Note
A naive implementation of this sort of time analysis might use actual, raw clock time, but consider the following two situations:
MYPROJECT-01 moves from “To Do” into “In Progress” at 4:55PM, just five minutes before the end of the day, then the next day moves from “In Progress” into “Done” at 9:05AM, five minutes after the beginning of the next day.
MYPROJECT-02 moves from “To Do” into “In Progress” at 10:00AM and in the same day from “In Progress” into “Done” at 4:00PM.
Clearly, MYPROJECT-02 was being “worked on” for more time than MYPROJECT-01, but let’s see how various algorithms might measure that time.
If we use raw clock time:
MYPROJECT-01: 16.2h (81 times more than the actual working time)
MYPROJECT-02: 6h
If we only measure time happening between 9A and 5P:
MYPROJECT-01: 0.2h (the actual working time)
MYPROJECT-02: 6h (the actual working time)
Of course, this does introduce one inaccuracy that may, depending on how predicable your team’s working hours are, make this behavior undesirable: time spent working on an issue outside of business hours isn’t counted. Typically, though, the amount of time an issue might be worked on outside those hours will be much smaller than the amount of excess time using raw clock time directly would count.
min_date
: The minimum date to add the business hours of to your interval. By default, 365 days before now.max_date
: The (exclusive) maximum date to add the business hours of to your interval. By default: tomorrow.start_hour
: The work day starting hour. Defaults to 9 (i.e. 9 AM)end_hour
: The work day ending hour. Defaults to 17 (i.e 5 PM)timezone_name
: The timezone to interpretstart_hour
andend_hour
in.work_days
: The days of the week to count as work days; 0 = Sunday,1 = Monday… 6 = Saturday.
Data Traversal¶
- extract(field: Iterable[Any], dotpath: str) Iterable[Any] ¶
For every member of
field
, walk through dictionary keys or object attributes described bydotpath
and return all non-null results as an array.Note
Although this will work, it is not necessary to use this for traversing into properties of grouped rows. If your selected field is an object having a value you’d like to select, you can simply use dotpath traversal to reach the value you’d like.
This function works for both dictionaries and objects.
- flatten_list(field: List[List[Any]]) List[Any] ¶
For a list containing a lists of items, create a single list of items from the internal lists.
The above is a little bit difficult to read, but in principle what this function does is convert values like:
[[1, 2, 3], [4, 5, 6]]
into a single list of the shape:
[1, 2, 3, 4, 5, 6]
Dates¶
- now(**replacements) datetime.datetime ¶
Return “now” as a timezone-aware
datetime.datetime
object.Additional parameters can be passed via keyword arguments; these values will be applied to the
datetime.datetime
object via itsreplace
method. See Python’s documentation for for more information .If you would like to obtain a timezone-unaware datetime object, pass
tzinfo=None
as a keyword argument.
- timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0) datetime.timedelta ¶
Returns a
datetime.timedelta
object.This object can be used in math with existing ``datetime.datetime``objects.
- datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0) datetime.datetime ¶
Returns a
datetime.datetime
object.To obtain a
date
object, call.date()
on the return value of this function.
Intervals¶
- empty_interval() portion.Interval ¶
- closed_interval() portion.Interval ¶
- open_interval() portion.Interval ¶
- openclosed_interval() portion.Interval ¶
- closedopen_interval() portion.Interval ¶
Json¶
- json_loads(json: str) Union[Dict, List] ¶
Parse a JSON string.
- json_dumps(obj: Union[Dict, List]) str ¶
Encode a dictionary or list into a JSON string.
Math¶
Basic¶
See more in information in Python’s Documentation.
- abs(value: float) str ¶
- max(values: List[Any]) Any ¶
- min(values: List[Any]) Any ¶
- pow(base: float, exponent: float, mod: Optional[int]) float ¶
- round(value: float, ndigits: int = 0) float ¶
- sum(values: List[Any]) Any ¶
Averages & measures of central location¶
See more in information in Python’s Documentation.
- mean(values: List[Any]) Any ¶
- fmean(values: List[Any]) float ¶
Requires Python 3.8
- geometric_mean(values: List[Any]) float ¶
Requires Python 3.8
- harmonic_mean(values: List[Any]) Any ¶
- median(values: List[Any]) Any ¶
- median_low(values: List[Any]) Any ¶
- median_high(values: List[Any]) Any ¶
- median_grouped(values: List[Any], interval: int = 1) Any ¶
- mode(values: List[Any]) Any ¶
- multimode(values: List[Any]) List[Any] ¶
Requires Python 3.8
- quantiles(values: List[Any], n=4, method=Literal['exclusive', 'inclusive']) Iterable[Iterable[Any]] ¶
Requires Python 3.8
Measures of spread¶
See more in information in Python’s Documentation.
- pstdev(values: List[Any], mu=Optional[float]) Any ¶
- pvariance(values: List[Any], mu=Optional[float]) Any ¶
- stdev(values: List[Any], xbar=Optional[float]) Any ¶
- variance(values: List[Any], xbar=Optional[float]) Any ¶
Numeric Representation¶
See more in information in Python’s Documentation.
- bin(value: int) str ¶
- hex(value: int) str ¶
- oct(value: int) str ¶
- ord(value: str) int ¶
List Operations¶
- union(iterable: Iterable[X]) X ¶
Types¶
See more in information in Python’s Documentation.
- bool(value: Any) bool ¶
- int(value: Any) int ¶
- set(value: Any) set ¶
- str(value: Any) str ¶
- tuple(value: Any) tuple ¶
- type(value: Any) str ¶
Sorting¶
See more in information in Python’s Documentation.
- reversed(iterable: List[Any]) Iterable[List[Any]] ¶
- sorted(iterable: List[Any]) Iterable[List[Any]] ¶
Filtering & Mapping¶
- simple_filter(iterable: Iterable[Any], **query_params: Dict[str, Any]) Iterable[Any] ¶
- simple_filter_any(iterable: Iterable[Any], **query_params: Dict[str, Any]) Iterable[Any] ¶
These functions provide you with a simple way of filtering lists using a syntax reminiscent of Django’s ORM query filter parameters.
simple_filter
: All providedquery_params
must match for the row to be returned.simple_filter_any
: At least one provided param inquery_params
must match for the row to be returned.
For example; to find issues having become resolved between two dates, you could run a query like the following:
select: "*" from: issues filter: - simple_filter( flatten_changelog(changelog), field__eq="resolution", toValue__ne=None, created__lt=parse_datetime("2020-02-02"), created__gt=parse_datetime("2020-02-01"), ) expand: - changelog
Consult the documentation for QueryableList for information about the full scope of parameters available.
Python Builtin Functions¶
See more in information in Python’s Documentation.
- filter(callable: Callable, Iterable[Any]) Iterable[Any] ¶
- map(callable: Callable, Iterable[Any]) Iterable[Any] ¶
Logic Shortcuts¶
See more in information in Python’s Documentation.
- all(iterable: List[Any]) bool ¶
- any(iterable: List[Any]) bool ¶
Counting¶
See more in information in Python’s Documentation.
- len(iterable: List[Any]) int ¶
You might be tempted to use
count()
given how we share many patterns with SQL, but this is what you actually want to use.
Ranges¶
See more in information in Python’s Documentation.
- range(stop: int) Iterable[int] ¶
- range(start: int, stop: int) Iterable[int] ¶
- range(start: int, stop: int, step: int) Iterable[int] ¶
Random¶
See more in information in Python’s Documentation.
- random() float ¶
- randrange(stop: int) int ¶
- randrange(start: int, stop: int) int ¶
- randrange(start: int, stop: int, step: int) int ¶
- randint(low: int, high: int) int ¶
- choice(Sequence[Any]) Any ¶