Eigene Jira-Skripte mit Report Builder: Teil 1 | Actonic GmbH

Eigene Jira-Skripte mit Report Builder: Teil 1

Report Builder: Was die App kann

Jedes Unternehmen, das mit Jira arbeitet, hat Bedarf an der Zeiterfassung und Berichterstellung. Mithilfe von Berichten und anhand von visuellen Echtzeit-Daten können Projektmanagement, Teamperformance, Prognosen und Fortschrittskontrollen verbessert werden. Für viele Organisationen sind individuelle Berichte sowie Teamberichte besonders wichtig, um die Produktivität zu überprüfen und Teams im Loop zu halten. Zusätzlich hat jede Firma einen individuellen Bedarf an Berichtvorlagen. Jira bietet out-of-the-box bereits einige Funktionen. Wer jedoch auf der Suche nach einer flexiblen und einzigartigen Lösung ist, für den ist Report Builder die richtige App!

Report Builder unterstützt Firmen dabei, unterschiedliche Berichte für sämtliche Jiradaten zu erstellen. Die App hält hierfür zahlreiche Berichtvorlagen bereit. In der Sektion Scripted Reports können zudem individuelle Berichte von Grund auf erstellt werden. Hierfür werden ein eigener Rechenalgorithmus, Parameter und Designbibliotheken verwendet. Erstellen Sie Ihren Bericht mit HTML und JavaScript Grundlagen.

Grundbausteine für die Berichterstellung in Report Builder

  1. Sie füllen die Parameter für Ihren Bericht aus: Umfang, Datum etc.
  2. Sie erhalten Rohdaten auf Basis der Parameter: Projekte, Tickets, Zeiterfassung etc.
  3. Sie nutzen einen Algorithmus, um Ihren Bericht mittels Rohdaten und Parameter zu erstellen
  4. Sie formatieren den Bericht so, dass er die Daten in einer Tabellen-, Diagramm-Form, etc. anzeigt

Los geht's mit der Berichterstellung

Unser Beispiel: Aufgewandte vs. geschätzte Zeit

Die Berichterstellung in Scripted Reports findet in drei Reitern statt:

  1. Allgemein
  2. Vorlage
  3. Skript

Im vierten Tab, Berechtigungen, können die erstellen Berichte mit anderen geteilt werden. Mehr dazu am Ende des Guides.

Werfen wir einen Blick in jeden Reiter und versuchen den Bericht mit Jiradaten basierend auf JQL Abfragen zu erstellen.

Reiter 1: Allgemein

In diesem Reiter können Sie die nötigen Felder und die Kategorie Ihres Berichts definieren. Momentan sind mehr als 10 Feldtypen vorhanden. Sie können diesen Schritt auch überspringen.

In unserem Fall wählen wir die Kategorie „Tabelle“ aus, um die aufgewandte und geschätzte Zeit für Tickets vergleichen zu können.

Sie erstellen zwei Eingabefelder:

  • JQL: für JQL Abfragen und Syntax-Checks
  • Feldwähler: Um die Felder der Zeiterfassung eines Jira Tickets auszuwählen

Sie füllen die Felder folgendermaßen:

  • Feldname: Sie wählen einen Feldnamen, der Teil des HTML Tags und der Skriptlogik wird.
  • Titel: Sie wählen einen Titel, der am oberen Rand des Feldes erscheint.
  • Typ: Sie wählen einen Typ, der das Verhalten des Feldes beeinflusst (Syntax Check oder Objektwahl).

Reiter 2: Vorlage

Um die finale Ansicht zu beeinflussen, ist in den Reiter Vorlage ein Handlebar Vorlagen Editor eingebaut. Wenn Sie damit nicht vertraut sind, werfen Sie einen Blick auf die offizielle Seite von Handlebars.

Wie bereits erwähnt, haben wir uns für das Tabellenformat entschieden. Wir gehen davon aus, dass eine Ansammlung von Aufgabenobjekten der Jira API vorhanden ist, wobei alle Objekte ihre eigene Zeile benötigen.

template_step_1

Reiter 3: Skript

Um die Daten zu verwalten, kann JavaScript im Allgemeinen Reiter verwendet werden. Der Code wird im Top-Level Wartebereich mit dem definierten SR Objekt ausgeführt. Das Objekt ist so konstruiert, dass es bei Interaktionen mit der Jira API, sowie beim Datenabruf der Eingabefelder des Allgemeinen Reiter und bei der Datenweiterleitung zur Vorlage unterstützt.

Hier die Methoden:

  • SR.getValueByFieldName(fieldName:String)  – zeigt den Wert des Eingabefeldes basierend auf dem fieldName string Parameter
  • SR.getIssuesByJQL(jql:String, <fields:String>) – zeigt die Sammlung der Jira Aufgaben basierend auf der JQL Abfrage und der Liste mit durch Komma getrennten Feldern an. Wenn die Parameter des Feldes nicht definiert sind, werden Jira Navigable Felder ausgegeben.
  • SR.render(templateValues:Object, <callback:Function>) – überträgt den Wert des JavaScripts in das Template Reiter. Dies kann entsprechend der Renderlogik mit einem Callback-Parameter definiert werden.

Sie füllen das JQL Feld und den Feldwähler aus. Dann müssen Sie die Daten in das Skript übertragen.

Screenshot from 2020-10-02 17-22-47

Um die Daten des Eingabefeldes mithilfe von SR.getValueByFieldName() zu erhalten, müssen Sie den Feldnamenwert übergeben, der im Allgemeinen Reiter festgelegt wurde. Hinweis: Beim Feldwähler ist eine Mehrfachauswahl möglich, die eine Sammlung an Werten ausgibt.

Da SR.getIssuesByJQL() den Zeichenfolgentyp für beide Parameter verwendet, können Sie die ausgewählten Felder in eine kommagetrennte Zeichenfolge umwandeln. Da die Abfrage der Jira API ein asynchroner Vorgang ist, sollten Sie den Top-Level Wartebereich verwenden.

Nun holen Sie die Jira Aufgaben und überwachen das Datenobjekt. Dies ist durch die JavaScript map() Methode möglich:

code_step_3

Die Aufgabe im letzten Code zeigt die Zeit in Millisekunden an. Sie müssen die Werte mittels Helferfunktion in Stunden umwandeln.

Diese Konversion muss nun auf die Map Callbackfunktion übertragen werden.

code_step_5

Nun übertragen Sie den Wert als Teil des Renderobjekts in die SR.render() Methode. Wenn Sie weitere Werte übertragen möchten, müssen Sie dem Renderobjekt zusätzliche Eigenschaften hinzufügen.

Nun erstellen Sie den Bericht mit dem Button „Bericht erstellen“ im oberen rechten Rand.

Hier der vollständige Code für das Skript Tab:

code_step_7

Die Berichtberechtigungen

Um die Berechtigungen für Ansicht, Bearbeitung und Admin Ihres Berichts anzupassen, nutzen Sie das Berechtigungs Tab. Der ausgewählte User sieht den geteilten Bericht im „Geteilt “ Reiter in Scripted Reports.

Momentan existieren drei mögliche Rollen für User:

1. Ansichtsmodus – der User kann den Bericht sehen, die Parameter oder die Einstellungen nicht verändern.
2. Bearbeitungsmodus – der User kann den Bericht einsehen und die Parameter verändern, jedoch keine neuen Einstellungen vornehmen.
3. Adminmodus – der User kann den Bericht sehen, die Parameter und Einstellungen verändern.

Der Ersteller eines Berichtes hat immer Adminrechte. Diese können nicht verändert werden.

Der Berechtigungsreiter erlaubt es, Berichte mit anderen zu teilen. Stellen Sie sicher, dass der entsprechende User auch Ansichtsrechte für den Bericht erhält.

Den Bericht speichern

Wenn Sie mit Ihrem Bericht zufrieden sind, speichern Sie diesen und vergeben Sie einen eindeutigen Namen.

Berichtverbesserungen: Skript anpassen

Sie wissen nun, wie Sie die tatsächliche Zeit mit der geschätzten Zeit für Jira Aufgaben berechnen können. Wie sieht der Prozess der Berichterstellung für ein gesamtes Projekt aus? Mit kleinen Veränderungen im Skript ist dies möglich.

Zunächst müssen Sie das Objekt benennen, dass jede Zeiterfassung für das Projekt als Schlüsselwert speichert.

Als Nächstes wählen Sie das Projektfeld im Feldwähler, das von der Jira API zurückgegeben wird. Definieren Sie den Rechenalgorithmus innerhalb des foundIssues.map() callback:

Übertragen Sie die „Projekt“ Objekt als Teil des Renderobjekts und updaten Sie den Vorlagenreiter.

code_step_10

Nun erhalten Sie die zusätzlichen Daten.

Screenshot from 2020-10-05 19-55-54

Hinweis: Sie haben den vorherigen Bericht nicht verloren, sondern zusätzliche Projektprotokolle erstellt. Das zeigt die Flexibilität von Scripted Reports, um Jiradaten auf unterschiedliche Weise auszuspielen.

Template:

<div class="issues-container">
    <h2>Issues work vs original estimate</h2>
    <table>
        <tr>
            <th>Ticket</th>
            <th>Actual work</th>
            <th>Original estimate</th>
            <th>Remaining estimate</th>
        </tr>
        {{#each issues}}
        <tr>
            <td>{{this.key}}</td>
            <td>{{this.timespent}} h</td>
            <td>{{this.timeestimate}} h</td>
            <td>{{this.remaining}} h</td>
        </tr>
        {{/each}}
    </table>
</div>
<div class="projects-container">
    <h2>Projects work vs original estimate</h2>
    <table>
        <tr>
            <th>Project Key</th>
            <th>Actual work</th>
            <th>Original estimate</th>
            <th>Remaining estimate</th>
        </tr>
        {{#each projects}}
        <tr>
            <td>{{this.key}}</td>
            <td>{{this.timespentHours}} h</td>
            <td>{{this.timeestimateHours}} h</td>
            <td>{{this.remainingHours}} h</td>
        </tr>
        {{/each}}
    </table>
</div>

Script:

/* 1. Define constants */
const projects = {};
/* 2. Define helper functions */
const millisecondsToHours = (milliseconds) => {
    if (isNaN(milliseconds)) {
        return 0;
    }
    return (Math.round(((milliseconds / 3600) + Number.EPSILON) * 100) / 100);
};
/* 3. Getting parameters for the JQL request */
const jqlValue = SR.getValueByFieldName('jql-field');
const selectedFields = SR.getValueByFieldName('fields-picker-field').join(',');
/* 4. Requesting issues by JQL string and fields list */
const foundIssues = await SR.getIssuesByJQL(jqlValue, selectedFields);
console.log('foundIssues', foundIssues)
/* 5. Parsing data from the response and creating the final object */
const issues = foundIssues.map(({ key, fields }) => {
    /* 5.1 Get properties:
        key - issue key
        projectKey - project key
        timespent - Time Spent
        timeoriginalestimate - Original estimate
        timeestimate - Remaining
    */
    const {
        project: {
            key: projectKey,
        },
        timeestimate,
        timeoriginalestimate,
        timespent,
    } = fields;
    /* 5.2 Projects work time vs original estimate logic  */
    if (projects[projectKey]) {
        /* 5.3  If project key is already exist then recalculate time */
        projects[projectKey].timeestimate += timeoriginalestimate;
        projects[projectKey].timespent += timespent;
        projects[projectKey].remaining += timeestimate;
        projects[projectKey].timeestimateHours = millisecondsToHours(projects[projectKey].timeestimate);
        projects[projectKey].timespentHours = millisecondsToHours(projects[projectKey].timespent);
        projects[projectKey].remainingHours = millisecondsToHours(projects[projectKey].remaining);
    else {
        /* 5.4  If project key is not exist then create object with initial time  */
        projects[projectKey] = {
            key: projectKey,
            timespent,
            timeestimate: timeoriginalestimate,
            remaining: timeestimate,
            timeestimateHours: millisecondsToHours(timeoriginalestimate),
            timespentHours: millisecondsToHours(timespent),
            remainingHours: millisecondsToHours(timeestimate),
        };
    }
    /* 5.5 Issue work time vs original estimate logic  */
    return {
        key,
        timespent: millisecondsToHours(timespent),
        timeestimate: millisecondsToHours(timeoriginalestimate),
        remaining: millisecondsToHours(timeestimate),
    };
});
/* 6. Passing values to the handlebars template engine */
SR.render({ issues, projects });

Die Berichtgalerie verwenden

Die integrierte Berichtgalerie beinhaltet zahlreiche Vorlagen. Diese können in der Galerie nicht bearbeitet werden, jedoch in den Bereich „Meine Berichte“ kopiert und dort angepasst werden.

Um den Bericht zu kopieren, drücken Sie auf  „Kopieren“ im oberen rechten Rand der Berichtansicht. Anschließend wird der ausgewählte Bericht automatisch im Reiter „Meine Berichte“ erstellt.

Report Building Service

Wenn Sie Bedarf an individuellen Berichtvorlagen haben, diese jedoch nicht selbst erstellen möchten, kontaktieren Sie unseren Report Building Service. Unser Team wird Ihnen einen individuellen Bericht erstellen. Lesen Sie hier mehr.

Individualisierte Report Builder Vorlage erhalten