mardi 7 novembre 2017
mercredi 7 juin 2017
JIRA Xporter plugin - tips and tricks
Tips for the plugin Xporter
- Print the current date
- Count number of issues in a JQL
- Sum a CF for all issues on a JQL
- Number of links
- Number of issues in a JQL (number format)
- Format a number with locale's decimal separator
- SUM Excel correct values in wrong locale format (workaround)
- Iteration on issue & subtasks
Print the current date
tool | Xporter |
---|---|
version | 5.0.0 |
use case | Print the current date |
Document generated at %{( new Date()).getDate() + "/" + (( new Date()).getMonth()+ 1 ) + "/" + ( new Date()).getFullYear()} |
Count number of issues in a JQL
tool | Xporter, excel |
---|---|
version | 5.0.0 |
use case | Count number of issues in a JQL |
Excel
JQL | <filter> |
---|---|
nfiches1 | ${jqlcount: |
nfiches2 | } |
usage
=CONCATENATE(nfiches1;<JQL>;nfiches2) |
example
${jqlcount:project = PJAB AND issuetype = Pénal AND created >= -90d AND status not in ("Constitution du dossier", "Saisir Avocat") AND Entité = "AXA Banque"} |
Sum a CF for all issues on a JQL
tool | Xporter, excel |
---|---|
version | 5.0.0 |
use case | Sum a CF for all issues on a JQL |
Excel
JQL | <filter> |
---|---|
CF | <cf name> |
nsomme1 | ${set(count,0)} #{for n=JQLIssuesCount|clause= |
nsomme2 | } #{if (%{'${JQLIssues[n]. |
nsomme3 | }' .length > 0})} ${set(count,%{${count} + ${JQLIssues[n]. |
nsomme4 | }})} #{end} #{end} %{Number(${count}).toFixed(2)} |
usage
in a excel cell :
=CONCATENATE(nsomme1;<JQL>;nsomme2;<CFname>;nsomme3;<CFname>;nsomme4) |
example
${set(count, 0 )} #{ for n=JQLIssuesCount|clause=project = PJAB AND issuetype = Pénal AND created >= -90d AND status not in ( "Constitution du dossier" , "Saisir Avocat" ) AND Entité = "AXA Banque" } #{ if (%{ '${JQLIssues[n].Créance comptable}' .length > 0 })} ${set(count,%{${count} + ${JQLIssues[n].Créance comptable}})} #{end} #{end} %{Number(${count}).toFixed( 2 )} |
Number of links
tool | Xporter, excel |
---|---|
version | 5.0.1 |
use case | Number of links matching a criteria (including creation date) |
- Count the links :
This requires to set a counter, and iterate over the links returned by "LinksCount". Filter on a date :
This requires to create different Date objects, either initiated with the date returned from the issue, or the calculated date. In the end we are indeed comparing milliseconds since 1-1-1970, so the > is enough./* test if the creation date is within the last 90 days */
(
new
Date(
'${dateformat("yyyy-MM-dd HH:mm:ss"):Links[n].Created}'
) > (
new
Date(
new
Date().setDate(
new
Date().getDate()-
90
))))
example
/* Set the variable countD4 to the number of links to the current issue "A" where : * link with the current is : "A" -(est modifié par)-> other issue * linked issue was creted less than 90 days ago * linked issue type is "Créa-Modif Document". */ ${set(countD4, 0 )} #{ for n=LinksCount|filter=%{( '${Links[n].LinkType}' .equals( 'est modifié par' )) && ( new Date( '${dateformat("yyyy-MM-dd HH:mm:ss"):Links[n].Created}' ) > ( new Date( new Date().setDate( new Date().getDate()- 90 )))) && '${Links[n].IssueTypeName}' .equals( 'Créa-Modif Document' )}} ${set(countD4,%{${countD4}+ 1 })} * ${Links[n].Key} ${Links[n].Summary} ${Links[n].IssueTypeName} ${Links[n].Status} #{end} ${countD4} |
Number of issues in a JQL (number format)
tool | Xporter, excel |
---|---|
version | 5.0.1 |
use case | Count the number of issues, and print it in excel JQL format |
- ${jqlcount:<JQL>} : counts the number of issues returned by the JQL
- %{Number(<...>)} : make sure it's rendered as a number in excel, for example to use it with =SUM() excel function
example
%{Number(${jqlcount:project = PJAB AND issuetype = Pénal AND created >= -92d AND status not in ( "Constitution du dossier" , "Saisir Avocat" ) AND Entité = "AXA Banque" })} |
Format a number with locale's decimal separator
tool | Xporter |
---|---|
version | ? |
use case | Format a number with specific locale's decimal separator |
use the ${numberformat("fr","#,##0.00#") function
example
${numberformat( "fr" , "#,##0.00#" ):JQLIssues[j].Montant de l'opération (en €)} |
SUM Excel correct values in wrong locale format (workaround)
tool | Excel, Xporter |
---|---|
version | 5.1.1 |
use case | Sum values that Xporter printed in the wrong locale. |
Sometime Xporter has some trouble printing numbers that are actually recognized as numbers by excel. An example is by
example
let's say you need to sum values in a locale where the decimal separator is ",", but xporter outputs a decimal separator as "."; hence the =SUM() function will not work.
=SUM(VALUE(CLEAN(SUBSTITUTE(C11; "." ; "," )));VALUE(CLEAN(SUBSTITUTE(C12; "." ; "," )));VALUE(CLEAN(SUBSTITUTE(C13; "." ; "," )))) |
Iteration on issue & subtasks
tool | Excel, Xporter, ScriptRunner |
---|---|
version | 5.1.1 |
use case | Bulk export, export the issues & insert their subtasks |
One way of doing so is to use the ScriptRunner JQL functions "subtasksOf(<key>)" and iterate on it.
example
#{ for j=JQLIssuesCount|clause=project = PRF and issueFunction in subtasksOf( "key = ${Key}" ) and issuetype NOT IN ( "Débit" , "Crédit" ) order by updated } |
vendredi 26 mai 2017
GMAIL - Google Script - auto delete old (junk) mail
Let's start with gmail since I have some needs I could not fit into the pretty basic filters that can be defined directly from the UI.
Context :
* I am able to identify labels are "search" (defined at the time the mail arrives) on my mailbox, that I want to delete after a certain period of time.
For instance, I might be interested in some promotions sent by some companies I have reductions cards with, but after X days, it's of no use and it's only taking precious space in my inbox.
* I do not wish to delete them directly, the "usual" trash with 30 days retention period is fine enought.
* I want to have this run every night.
1. Go to https://script.google.com
2. Enter this script :
- set the variable delayDays
- set the variable mySearch accordingly to your needs.
function cleanUp() {
/**
* @var delayDays int : Enter # of days before messages are moved to trash
* @var mySearch string : Enter the search as tested in Gmail search bar.
*/
var delayDays = 30 ;
var mySearch = 'label:online_store-totoStore OR from:bonsplans@newsletter.travel.com .com OR label:paris-freecycle';
//-- code
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-delayDays);
var tmp_threads = GmailApp.search(mySearch);
var threads = [];
var threads = threads.concat(tmp_threads);
for (var i = 0; i < threads.length; i++) {
if (threads[i].getLastMessageDate()
{
threads[i].moveToTrash();
}
}
}
EDIT 2020 : https://github.com/copolycube/GmailPurgeEmailsMatchingQuery
- array of simplier queries to find
- does multiple search instead of 1 massive one
- concatenates a "avoid deletion", for example to keep "starred" e-mail (by default)
3. Set it up to run every night :
3.1 : select the 'clock/trigger'.
3.2 : add the desired triggers on the popup window that will appear.
Other solution (easier)
lundi 13 février 2017
JIRA server tips and tricks : WF properties on Status
(note : this works on JIRA Server / Datacenter at the time of the writing of this, Feb 2017)
Sometimes, you want to set some permissions directly depending on the JIRA Issue Workflow status (i.e. not depending on the whole Permissions scheme applied at the whole project level).
This hard to find trick consist in using Workflow properties on the WF steps.
Use-case : lock the "edit" button on 1 WF step, to 3 user groups
ref :
- https://confluence.atlassian.com/adminjiraserver072/workflow-properties-828788054.html
- https://jirasupport.wordpress.com/2016/02/23/workflow-properties/
so
that only JIRA administrators can edit an issuejira.permission.edit.group=jira-administrators |
jira.permission.edit.group. 1 =jira-administrators jira.permission.edit.group. 2 =jira-fr jira.permission.edit.group. 3 =jira-pmo |
This of course needs to be applied for each step/status of the workflow impacted, making sure that this WF is not shared with other projects or other IT.
JIRA : Status Permissions
Please note that the expected format is not <property> = denied This format actually grants access to the user named denied.
The proper format is is:
<property>.denied = whatever
The complete format is:
<permission key> = jira.permission[.subtasks].<system project permission>.<grant type>[.<suffix>] <system project permission> = assign | assignable | attach | attachdeleteall | attachdeleteown | browse | close | comment | commentdeleteall | commentdeleteown | commenteditall | commenteditown | create | delete | edit | link | managewatcherlist | modifyreporter | move | project | resolve | scheduleissue | setsecurity | transition | viewversioncontrol | viewvotersandwatchers | viewworkflowreadonly | work | worklogdeleteall | worklogdeleteown | worklogeditall | worklogeditown <grant type> = denied | groupCF | assignee | assigneeassignable | reporter | reportercreate | userCF | applicationRole | group | lead | projectrole | user <suffix> = any text that makes the permission key unique among all keys of permissions in the same workflow step. |
lundi 30 janvier 2017
JIRA Script Runner / split cascading field in two text fields
Script Runner 4.3.16
import
com.atlassian.event.api.EventListener
import
com.atlassian.jira.component.ComponentAccessor
import
com.atlassian.jira.event.issue.AbstractIssueEventListener
import
com.atlassian.jira.event.issue.IssueEvent
import
com.atlassian.jira.event.type.EventDispatchOption
import
com.atlassian.jira.issue.IssueManager
import
com.atlassian.jira.issue.CustomFieldManager
import
com.atlassian.jira.issue.MutableIssue
import
com.atlassian.jira.issue.fields.CustomField
import
org.apache.log4j.Logger
def
cfNameCascading =
"Cascading Field Name"
;
def
cfNameFirst =
"Cascading Field Name : part 1"
;
def
cfNameSecond =
"Cascading Field Name : part 2"
;
IssueManager issueManager = ComponentAccessor.getIssueManager()
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
/** Get the different CF objects**/
CustomField cfCascading = customFieldManager.getCustomFieldObjectByName(cfNameCascading)
CustomField cfFirst = customFieldManager.getCustomFieldObjectByName(cfNameFirst)
CustomField cfSecond = customFieldManager.getCustomFieldObjectByName(cfNameSecond)
/** Get Cascading Field values (map) **/
Map cfVal = issue.getCustomFieldValue(cfCascading)
as
Map
if
(cfVal) {
String valFirst = cfVal.
get
(
null
);
String valSecond = cfVal.
get
(
"1"
);
List allValues = cfVal.values()
as
List;
log.info(
"First - second: $valFirst - $valSecond"
);
log.info(
"All: $allValues"
);
/** Set each separate CF **/
issue.setCustomFieldValue(cfFirst, valFirst);
issue.setCustomFieldValue(cfSecond, valSecond);
/** update the issue **/
issueManager.updateIssue(event.getUser(), issue, EventDispatchOption.DO_NOT_DISPATCH, false);
}
else
{
log.info(
"Custom field not present on this issue"
)
}
vendredi 6 janvier 2017
JIRA Server - redirect to full issue view after creation
(note : this works with most version of JIRA available as of now, Janvier 2017)
Instead of staying in the current view, you can use the following tricks to make your JIRA SERVER / DATACENTER redirect directly to the recently created issue.
JavaScript (in banner)
The trick here is to disable the "popup" with the create screen, hence going to the "full create screen" which opens the page afterward.
<script type= "text/javascript" > AJS.$( "#create_link" ).removeClass( "create-issue" ); $( "#announcement-banner" ).hide()</script> |
cf. : https://confluence.atlassian.com/jirakb/how-to-disable-create-issue-popup-300813780.html
Plugin "Issue Quick Start" (and JS source)
The following plugin aims at the same :
- Marketplace : https://marketplace.atlassian.com/plugins/uk.org.sappho.jira.plugin.jira-plugin-goto-new-issue/versions
- GitHub (License : MIT) : https://github.com/sappho/jira-issue-quick-start
Excerpt from the source :
AJS.$(document).on( 'DOMNodeInserted' , function (event) { if (event.target.id == 'aui-flag-container' ) { console.log( 'issue-quick-start: Got post-it note!' ); AJS.$(event.target).on( 'DOMNodeInserted' , function (event) { console.log( 'issue-quick-start: Post-it HTML: ' + event.target.innerHTML); var postItLink = AJS.$(event.target.innerHTML).find( 'a' ); var postItPath = postItLink.attr( 'href' ); if (postItPath && postItLink.attr( 'data-issue-key' )) { console.log( 'issue-quick-start: Going to new issue path ' + postItPath); window.location = postItPath; } }) } }); |
[TODO] Script Runner : redirect with script fragment (??)
- Is there some cleaner way to do it with SR "Script Fragments" to perform the redirect ?
cf. https://scriptrunner.adaptavist.com/4.3.7/jira/fragments/WebItem.html#_redirects
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.label.LabelManager import com.atlassian.sal.api.ApplicationProperties import com.onresolve.scriptrunner.runner.ScriptRunnerImpl import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate import groovy.transform.BaseScript import javax.ws.rs.core.MultivaluedMap import javax.ws.rs.core.Response @BaseScript CustomEndpointDelegate delegate def labelManager = ComponentAccessor.getComponent(LabelManager) def applicationProperties = ScriptRunnerImpl.getOsgiService(ApplicationProperties) def issueManager = ComponentAccessor.getIssueManager() labelIssue(httpMethod: "GET" ) { MultivaluedMap queryParams -> def issueId = queryParams.getFirst( "issueId" ) as Long def issue = issueManager.getIssueObject(issueId) /** def label = labelManager.getLabels(issueId) if (! label) { labelManager.addLabel(null, issueId, "approved", false) } **/ Response.temporaryRedirect(URI.create( "${applicationProperties.baseUrl}/browse/${issue.key}" )).build() } |
mardi 26 juillet 2016
alternatives github
- Pour un GitHub plus démocratique et efficace http://framablog.org/2013/05/
12/github-on-a-un-probleme/
- GitPrep, un clone libre de GitHub ( http://linuxfr.org/users/
philippemc/journaux/gitprep- un-clone-libre-de-github )
- http://git-scm.com/book/ch4-6.
html Git on the Server - Smart HTTP
- http://git-scm.com/book/ch4-8.
html Git on the server – gitlab : http://gitlab.org/
- Bitbucket
- GITlab
lundi 4 juillet 2016
Dimming screen / "night mode" for android or mac
- Mac OS X : F:lux https://justgetflux.com/
- Android : Twilight https://play.google.com/store/apps/details?id=com.urbandroid.lux
mardi 14 juin 2016
MySQL to CSV file
SELECT order_id,product_name,qty
FROM orders
WHERE foo = 'bar'
INTO OUTFILE '/tmp/orders.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
TODO : There is still to find how to print the column titles along with the result...
lundi 29 février 2016
VirtualBox install / config de base
* Config Clavier Mac sous Debian :
https://lokan.fr/2013/12/11/configurer-correctement-son-clavier-mac-sous-debian/
XKBMODEL="pc105"
XKBLAYOUT="fr"
XKBVARIANT="mac"
XKBOPTIONS="lv3:switch,compose:lwin”
* Config redimensionner fenêtre VirtualBox
(non testé ) : https://forums.virtualbox.org/viewtopic.php?f=2&t=68966
* http://download.virtualbox.org/virtualbox/5.0.14/
* http://download.virtualbox.org/virtualbox/5.0.14/UserManual.pdf
* Be sure to have the kernel headers, gcc and other basic build-tools installed
apt-cache search/apt-get install build-essential linux-headers-$(uname -r) dkms
* If present, it should be in /media/cdrom (or /media/cdrom0)
cd /media/cdrom0
sh VBoxLinuxAdditions.run
* NB : you might need to reboot the host.
* You can now add some shared folders (for example). They will be owned by root with drwxrwx--- root vboxsf (group)
* Add user in group vboxsf :
sudo usermod cm -a -G vboxsf