dannyman.toldme.com


JIRA

JIRA Cascading Select in Jython

Link: https://dannyman.toldme.com/2012/12/06/jira-cascading-select-in-jython/

The Cascading Select Custom Field type in JIRA is a bear. The first trick is learning to set the “null” value and then the “1” child value. The next trick is building out a ModifiedValue object to hold your change. Then you get to jump down the rabbit hole of finding the correct Option values for the custom field, and setting them with the tricks just mentioned.

So, in the interests of saving me sanity next time I need to set a Cascading Select, here’s a Jython function that works in Jira 4.2:

import logging
 
from com.atlassian.jira import ComponentManager
from com.atlassian.jira.issue.customfields.manager import OptionsManager
from com.atlassian.jira.issue.customfields.view import CustomFieldParamsImpl
from com.atlassian.jira.issue import ModifiedValue
from com.atlassian.jira.issue.util import DefaultIssueChangeHolder
from java.util import HashSet
 
# cf = custom field
# issue = issue to modify
# parent = top value to set (string value)
# child = child value to set (string value)
def set_cascading_select(cf, issue, parent, child):
    # Get the managers
    cfm = ComponentManager.getInstance().getCustomFieldManager()
    om = ComponentManager.getComponentInstanceOfType(OptionsManager)
    fli = ComponentManager.getInstance().getFieldLayoutManager().getFieldLayout(issue).getFieldLayoutItem(cf)
 
    parent_options = om.getOptions(cf.getRelevantConfig(issue))
    parent_option = None
    child_option = None
    try:
        parent_option = parent_options.getOptionForValue(parent, None)
    except:
        pass
    try:
        child_option = parent_options.getOptionForValue(child, parent_option.getOptionId())
    except:
        pass
 
    if parent_option and child_option:
        old_application = issue.getCustomFieldValue(cf)
        new_application = CustomFieldParamsImpl(cf)
        a_none = HashSet()
        a_none.add(parent_option)
        a_1 = HashSet()
        a_1.add(child_option)
        new_application.put(None, a_none)
        new_application.put("1", a_1)
        mf = ModifiedValue(old_application, new_application)
        cf.updateValue(fli, issue, mf, DefaultIssueChangeHolder())
        logging.debug("set issue " + issue.getKey() + " cf " + cf.getName() + " setting " + parent + "/" + child)
        return True
    else:
        logging.error("invalid parent/child option: " + parent + "/" + child)
        return None

Example function calls from within a validation hook:

cfm = ComponentManager.getInstance().getCustomFieldManager()
 
application_cf = cfm.getCustomFieldObjectByName("Beverages")
 
# good
set_cascading_select(application_cf, issue, "Hard Drinks", "Whiskey")
# bad child
set_cascading_select(application_cf, issue, "Hard Drinks", "Coke")
# bad parent
set_cascading_select(application_cf, issue, "Soft Drinks", "Whiskey")
# total crap
set_cascading_select(application_cf, issue, "Illicit Drugs", "Bath Salts")

The logging stuff is useful for debugging, if you have that set up, else just remove those bits.

Feedback Welcome


Excerpts, Letters to The Man, Technical

Collocation vs Colocation

Link: https://dannyman.toldme.com/2012/12/03/collocation-vs-colocation/

This drives me insane. Part of the challenge is that most software dictionaries are unaware of the word “colocation” and are happy to offer “collocation” as an alternative, but that is wrong wrong wrong wrong and it makes me a little nuts every time.

So, here is some explanation I just sent to the NOC and copied to the Sales team of a “Colocation Provider” who keeps sending me messages from something called “Collocation Status Report”:

Dear NOC:

A collocation is a statistic used by linguists to determine the
frequency with which words and phrases are found together.

On your contact information page, there is an option to contact Sales
about “Colocation”

Assuming that you are indeed in the business of Colocation, and not
actually updating us on the status of word frequencies, please fix the
name in your outgoing envelope from “Collocation Status Reports” to
“Colocation Status Reports”

Thanks,
-danny

Yup. That’s all I have to say about that.

1 Comment


Technical, Technology

Amazon Hack: Reverse Showrooming

Link: https://dannyman.toldme.com/2012/11/12/amazon-hack-reverse-showrooming/

Modern retailers have a challenge we have come to call “showrooming” where a consumer visits the local store to try out a product, then they go and order the product off Amazon.com or another retailer for less money. Some retailers will do online price matching, which is reasonable because even though that lowers their margin, they still get the sale, and can upsell you a few accessories. I saved a few dollars this way while buying a TV from Fry’s.

However, I was just browsing Amazon.com for a resin adirondack chair, where I saw:

Twenty six bucks!? Sounds good . . . not eligible for Prime, so let’s check the shipping . . .

Whiskey . . . Tango . . . Foxtrot . . . $192 shipping you say?!! Something is fishy here . . .

So, I surf on over to True Value’s web site, where the chairs are $20, and they’ll ship to the local store.

Which makes me wonder if this is a case of “reverse showrooming” . . . I go to Amazon.com because I can probably find what I am looking for, then I am led to a local retailer to save money. Very clever . . .

Feedback Welcome


JIRA, Technical

JIRA Workflow Transition Condition: check_parent_resolved.py

Link: https://dannyman.toldme.com/2012/11/07/jython-workflow-transition-check-parent/

It took a few hours to figure this hook out, so I’m including my hard-won lines of code here.

# -*- coding: UTF-8 -*-
# Check if PARENT is resolved.
# Monitoring creates Events in the Event queue, these Events
# automatically create Incident children.
# We don't want to resolve any Incident children until the parent Event
# resolves.
# 
# (Normally you want to block on your children instead of your parent.)
 
from com.atlassian.jira import ComponentManager
from com.atlassian.jira.issue.link import IssueLinkManager
 
ilm = ComponentManager.getInstance().getIssueLinkManager()
 
# Assume we are okay ...
result = True
 
for link in ilm.getInwardLinks(issue.getId()):
    if link.getIssueLinkType().getName() == "Parent" and link.getSourceObject().getResolution() == None:
        result = False

Feedback Welcome


Technical

HOWTO: Style a Multi-Column List in HTML

Link: https://dannyman.toldme.com/2012/10/22/html-css-ul-li-columns/

For a project at work I was asked if I could lay out a long table-of-contents in our CMS as multiple columns. Logically enough, the table-of-contents renders as a flat, unordered list (UL) of list items (LI) … arranging that as multiple columns is a preposterous idea! Of course, preposterous questions can be very interesting.

So, we start with something like this:

		<h1>Yadda Yadda</h1>
 
		<ul class="columns">
			<li>BLAH blah blah one</li>
			<li>BLAH blah blah two</li>
			<li>BLAH blah blah three</li>
			<li>BLAH blah blah four</li>
			<li>BLAH blah blah five</li>
			<li>BLAH blah blah six</li>
			<li>BLAH blah blah seven</li>
			<li>BLAH blah blah eight really long example</li>
			<li>BLAH blah blah nine</li>
			<li>BLAH blah blah ten</li>
			<li>BLAH blah blah eleven</li>
			<li>BLAH blah blah twelve</li>
		</ul>
 
		<h1>Yappa Yappa</h1>

Which renders as:

Yadda Yadda

Yappa Yappa

Okay. So, multiple columns? That is what TABLE is for, but we are dealing with UL. But I have my old friend the float attribute, so I define a style sheet:

			ul.columns li {
				list-style: none;
				float: left;
				width: 30%;
			}
			h1 { clear: left; }

Now we render as three columns:

Yadda Yadda

Yappa Yappa

Two columns? Four? Just tune the width attribute:

			ul.columns li {
				list-style: none;
				float: left;
				width: 45%;
			}
			h1 { clear: left; }

And you get:


It is not perfect. The content renders left-to-right, so strictly speaking, these aren’t columns.

If you narrow enough to cause wrapping you get some odd gaps.

Due to margins and the like, you can’t just say three columns is 33%, two columns is 50% . . . if you narrow this page enough you’ll see a graceful degradation from more columns to fewer.

Here’s a more dynamic example, where wider pages will get more columns:

			ul.columns li {
				list-style: none;
				float: left;
				width: 15em;
			}
			h1 { clear: left; }

Renders as:


The block following your UL will need to clear the floated content, hence the clear: left on the H1, above.

See also:

Feedback Welcome


JIRA, Python, Technical

Jython Validator Cookbook

Link: https://dannyman.toldme.com/2012/09/25/jython-validator-cookbook/

Building on a previous post to validate user time tracking, a few “cookbook” scripts that may be handy to you or me in the future.

JIRA 4.2, YMMV.

Validate User Time Tracking

Somewhat elaborate: enforce that time worked has been logged, except under certain circumstances. See original post.

import com.atlassian.jira.issue.worklog.Worklog
from com.atlassian.jira import ComponentManager
 
# Time Already Logged
timespent = issue.getTimeSpent()
# Time Logged via current screen
try:
    timelogged = dict(issue.getModifiedFields())['worklog']
except:
    timelogged = False
 
# Duplicate Issue?  It is as good as logged!
resolution = issue.getResolution()
if resolution['name'] == "Duplicate":
    timelogged = True
if resolution['name'] == "Self Corrected":
    timelogged = True
 
# Nagios likes to close tickets, but doesn't get paid
user = ComponentManager.getInstance().getJiraAuthenticationContext().getUser()
if user.getName() == "nagios":
    timelogged = True
 
if timespent < = 0 and timelogged == False:
    result = False
    description = "Please log the time you spent on this ticket."

Assign Unassigned Issue to User

This helps make sure tickets get assigned.

# -*- coding: UTF-8 -*-
from com.atlassian.jira import ComponentManager
 
assignee = issue.getAssignee()
user = ComponentManager.getInstance().getJiraAuthenticationContext().getUser()
 
if not issue.getAssignee():
    issue.setAssignee(ComponentManager.getInstance().getJiraAuthenticationContext().getUser())

Validate Custom Field Value

We have a particular custom field which can be set UNKNOWN by the Reporter, but which should be cleaned up by the Assignee.

from com.atlassian.jira import ComponentManager
 
cfm = ComponentManager.getInstance().getCustomFieldManager()
product = issue.getCustomFieldValue(cfm.getCustomFieldObject('customfield_12345'))
 
if product == 'UNKNOWN':
    result = False
    description = "Please set CUSTOM_FIELD value appropriately."

Feedback Welcome


Linux, Sundry, Technical, Technology, Testimonials

Smoothing Out Fonts on kubuntu-desktop

Link: https://dannyman.toldme.com/2012/09/13/make-kubuntu-kde-fonts-pretty/

So, I really like Ubuntu. Its Linux and it just mostly works. Except when they try to force everyone into some experimental new desktop environment. That is pretty awful, but I’m happy again now that I switched to kubuntu-desktop. (apt-get install kubuntu-desktop)

Kubuntu is Ubuntu with a nicely set-up KDE environment. They try to get you to use their own home-grown web browser, and the file manager takes some getting used to, but you can pretty quickly get under the hood, set up all your little window manager preferences, and get back to jamming. (Focus Follows Mouse in my house!)

The only thing that was missing is the fonts were rendering . . . not as pretty as regular Ubuntu. Kubuntu is set up to use the Ubuntu font, but in KDE things render kind of pixelly looking, like I was still in the 90s. A bit of searching and they seem to look nicer:

System Settings > Application Appearance > Fonts
Use anti-aliasing: Enabled
Configure…
Use sub-pixel rendering: RGB
Hinting style: Slight

Now things feel a little more 21st century.

Feedback Welcome


Linux

Merge Multiple PDF Documents

Link: https://dannyman.toldme.com/2012/06/09/e-pdfs-unum/

By the way, let’s say you have to print, sign, scan, and email back a document using the flatbed scanner attached to your Linux workstation. No trouble scanning in each page as an individual PDF document using XSane, but assembling the multipage project into one document proved a modest challenge last time I tried.

Solution? Install pdftk, and then the command goes something like:

pdftk AVID000*.pdf cat output AVID.pdf

That’s will merge all the documents named AVID0001.pdf, AVID0002.pdf, etc into AVID.pdf. Or to put it another way:

pdftk <a bunch of PDF files> cat output <target PDF file>

Cheers!

1 Comment


Linux, Technical, Technology, Testimonials

Dear Ubuntu: Stop Asking me for my Password!

Link: https://dannyman.toldme.com/2012/05/24/ubuntu-password-prompt/

I like Ubuntu. Or at least I liked it a few years back when you got a very nice functional desktop out of the box … but that is a different gripe.

I really like an OS that updates the software for me. Really, downloading and installing updates is for chumps! Way to go, Ubuntu!

But here’s a feature that has been bugging me for years: the system pops up a window saying “hey, I’m going to update the system software for you.”

And I’m like “sure, go ahead, be my guest!”

Then it’s like “okay, please give me the administrator password.”

And I’m like “well, okay . . . but . . .”

Each time the computer pops up a window unbidden offering to do me a favor in exchange for my password, I am wondering when some bastard will get around to writing a bit of malware that offers to do something nice for me in exchange for my password. Maybe a web site can launch a convincing-looking software-update window and prompt me for a password. It is going to be epic just how many users can be convinced to type their system passwords into a malware site.

If you are a part of the system that has access to do heavy lifting, please do not approach the user asking for a password. It teaches the user that “hey, its normal for your computer to pop up some window and ask for a password and when that happens you should totally humor the computer and give it your password.” You want to update my software? Great! You want to check with the person using the computer to make sure its a good time? Great, ask away, tell them your plans. But when they say yes, just fricking do it, and don’t ask them for their password.

1 Comment


FreeBSD, JIRA, Linux, Mac OS X, Technical

Strip Non-Ascii From a File

Link: https://dannyman.toldme.com/2012/05/10/mysql-not-configured-for-utf8/

I have had bad luck trying to coax this out of Google, so here’s a Perl one-liner:

perl -pi -e 's/[\x80-\xEF]//g' file.txt

Where file.txt is a file you want to clean up.

Why this comes up is because we have a web application that was set up to hit a MySQL database, which is incorrectly configured to store text as ASCII instead of UTF-8. The application assumes that all text is Unicode and that the database is correctly configured, and every week or two someone asks me why they are getting this weird gnarly error. Typically they are pasting in some weird UTF-8 whitespace character sent to us from Ukraine.

Eventually the database will be reloaded as UTF-8 and the problem will be solved. Until then, I can tell folks to use the Perl command above. It just looks for anything with the high bit set and strips it out.

Feedback Welcome


JIRA, Technical

Work Note: Quarterly Reports from MySQL

Link: https://dannyman.toldme.com/2012/05/08/jira-sla-sql-query/

I am not a DBA. I am but a humble SysAdmin who gets asked to figure out things like “how have we been at meeting our SLAs over time?” After I try to excuse myself I’ll inevitably end up say at the JIRA database running a query like this:

echo
echo "Incidents (P3)"
mysql -u jira jiradb< <__id3q
select year(created) as "Year", quarter(created) as "Quarter",
    count(pkey) as "Total",
    sum(resolutiondate < date_add(created, interval x day)) as "Met SLA",
    sum(resolutiondate < date_add(created, interval x day)) / count(pkey) as "SLA %%"
    from jiraissue where pkey like 'OPS-%' and priority = 3
    and assignee != 'nagios' and issuetype = 26
    group by year(created), quarter(created) order by created;
__id3q

That above is a fragment from a shell script. Shell scripts are great for complex SQL queries, I find. Set a value x at interval x day and the output looks something like:

Incidents (P3)
Year    Quarter Total   Met SLA SLA %%
2011    2       xxx     xxx     x.xxxx
2011    3       xxx     xxx     x.xxxx
2011    4       xxx     xxx     x.xxxx
2012    1       xxx     xxx     x.xxxx
2012    2       xxx     xxx     x.xxxx

The query does some things that are newer to my limited understanding of SQL. For me the magic bits are sum()ed columns and the availability of quarter() … you can do monthly reports just as easily with month(). I’d love to concatenate Year-Month into a string like “2012-05” but for the purposes of making my boss a little happier queries like this are good to have in the locker.

Some day I’ll be hip enough to convert things like this into JIRA widgets.

Oh yeah, and if your SLAs are measured in “business hours” or “business days” this will give you only a crude understanding of how well you have met your SLAs … an accurate measure would probably get embedded in a handler that gets called on issue close which can evaluate SLA fulfillment per issue priority and the local work schedule.

1 Comment


About Me, Technical, Technology

Usability Example: Unsubscribe FAIL

Link: https://dannyman.toldme.com/2012/03/07/linkedin-unsubscribe-not/

Last week I dug through several menus to try and unsubscribe from all the spam LinkedIn sends me. Today I got another email and at the bottom was an “unsubscribe” link that I clicked on. Here’s what I got:

Unsubscribe screen doesn't actually unsubscribe . . .

Not only is this not an unsubscribe feature, LinkedIn gets bonus points for trying to sign you up for MORE e-mail.

Instead of screwing around with the half-dozen sub-menus again, I dropped them a feedback saying that this burns up good will, damages the brand, discourages me from engaging, and may in time lead me to delete my profile. There’s more than one way to search for resumes online.

2 Comments


Python, Sundry, Technology

PYCON Starts March 7th!

Link: https://dannyman.toldme.com/2012/02/08/derr/

From an email to colleagues:

Yesterday I got Dr Sick Wife to drop me off at the Santa Clara Convention Center so Mr Sicker Light-Headed Husband wouldn’t miss any PYCON. After an awkward twenty minutes of asking people there for the lighting and LED conference where the Python was, I checked my smart phone and noted that … PYCON is *MARCH* 7th.

So I took the light rail home and told $BOSS I was on PTO (well, I call it MLK day due to Puppet Training) I then slept a lot, and did other things sick people do that don’t bear repeating in a professional context, and watched Dr Who save the Earth on TV, slept some more, and I am feeling way better today, which means I feel regular sick, not super sick.

So, I’ll be WFH today. Trust me, whatever this is, you’re lucky to miss out! I don’t normally get sick so this is a novel experience … I’ll likely be seen in the office next week, though if I’m coughy or sneezy I’ll keep that train wreck at home, because, as you might gather, you don’t want a piece of this!

If you’re attending PyCon, I look forward to seeing you there … next month! Hopefully I won’t be light-headed!

Feedback Welcome


Linux, Technical

Notes: Make Ubuntu GUI Less Annoying

Link: https://dannyman.toldme.com/2011/12/07/ccsm-ubuntu-gui-tweaks/

First, install ccsm.

Terminal windows resize themselves stupidly when changing font size. I don’t know how to fix that, but in the CompizConfig Settings Manager, I can enable Resize Info to overlay the dimensions of any window as I resize it.

Often, when dragging a window around, it tries to go full-screen on me. This is obnoxious! Just disable Grid in CCSM.

If anyone knows how to reconcile Focus Follows Mouse with “menu bar at the top of the screen” I would love to hear it! Or if you know how to configure the pager to something besides 2×2 …

1 Comment


JIRA

JIRA: Require User Time Tracking

Link: https://dannyman.toldme.com/2011/12/01/jira-jython-validator-enforce-time-spent/

Time tracking in JIRA is a nice feature, but we have to get people to do it. My initial attempts to enforce time tracking ran into trouble, but I was able to develop a Jython Validator to hook on to transitions to the Resolved state. Now it is mandatory for our users to log time worked before they can resolve an issue:

# -*- coding: UTF-8 -*-
import com.atlassian.jira.issue.worklog.Worklog
from com.atlassian.jira import ComponentManager
 
# Time Already Logged
timespent = issue.getTimeSpent()
# Time Logged via current screen
try:
    timelogged = dict(issue.getModifiedFields())['worklog']
except:
    timelogged = False
 
# Duplicate Issue?  It is as good as logged!
resolution = issue.getResolution()
if resolution['name'] == "Duplicate":
    timelogged = True
if resolution['name'] == "Self Corrected":
    timelogged = True
 
# Nagios likes to close tickets, but doesn't get paid
user = ComponentManager.getInstance().getJiraAuthenticationContext().getUser()
if user.getName() == "nagios":
    timelogged = True
 
if timespent < = 0 and timelogged == False:
    result = False
    description = "Please log the time you spent on this ticket."

2012-01-24 Update: the script now contains additional logic, which exempts the nagios user from enforcement and allows resolution of duplicated or self-correcting issues which may not require time tracking. Hopefully this example is useful to somebody.

4 Comments

« Newer Stuff . . . Older Stuff »
Site Archive