Skip to content

Commit

Permalink
Added new component displays; refactored display settings; minor UI t…
Browse files Browse the repository at this point in the history
…weaks
  • Loading branch information
loganbibby committed Feb 9, 2023
1 parent 42953d5 commit 5ce29e4
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 60 deletions.
49 changes: 27 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,48 +42,52 @@ A Personal Access Token (PAT) created from [here](https://id.atlassian.com/manag

API endpoint for Jira. Automatically set if not defined.

### `ISSUE_DEFAULT_STATUS_COLORS`
### `ISSUE_DEFAULT_DISPLAY_SETTINGS`

Dictionary of colors for each status where the key is sluggified status name and the value is the color.
These settings are the defaults for how the organizer displays issues. This same dictionary format is used for defining a view's `display` settings.

You can override this in `ISSUE_VIEWS` with `status_colors`.
#### `flags`

### `ISSUE_DEFAULT_PRIORITY_COLORS`
List of flags for displaying components in the issue:

Dictionary of Bootstrap color classes for each priority where the key is the sluggified priority name and the class name.
* `show_status`: Shows the issue's status
* `show_issue_type`: Shows the issue type
* `show_reporter`: Shows the name of the report (only on the main column)
* `show_project`: Shows the project name (only on the main column)
* `show_assignee`: Shows the assignee name (only on the main column)
* `show_priority`: Shows the priority (only on the main column)
* `show_labels`: Shows a comma separated list of labels (only on the main column)
* `show_parent`: Shows the parent Jira information (only on the main column)

Defaults to `danger` for `high` and `very_high`, `warning` for `medium`, and `primary` for `low` and `lowest`.
#### `other_statuses`

You can override this in `ISSUE_VIEWS` with `priority_colors`.
List of sluggified status names to be shown in the Other column.

### `ISSUE_DEFAULT_OTHER_STATUSES`
#### `statuses`

List of sluggified status names to be shown in the Other column.
Dictionary of settings for each status where the key is sluggified status name and the value is a dictionary:

You can override this in `ISSUE_VIEWS` with `other_statuses`.
* `color`: Bootstrap color class

### `ISSUE_DEFAULT_DISPLAY_FLAGS`
#### `priorities`

Dictionary of display flags where the key is an option from below and the value is a boolean.
Dictionary of settings for each priority where the key is sluggified priority name and the value is a dictionary:

* `show_status`: Shows the issue's status
* `show_reporter`: Shows the name of the report (only on the main column)
* `show_project`: Shows the project name (only on the main column)
* `show_assignee`: Shows the assignee name (only on the main column)
* `show_priority`: Shows the priority (only on the main column)
* `color`: Bootstrap color class

#### `issue_types`

Dictionary of settings for each issue type where the key is sluggified issue type and the value is a dictionary:

You can override this in `ISSUE_VIEWS` with `display_flags`.
* `icon`: Font Awesome 5 Free icon class

### `ISSUE_VIEWS`

Dictionary of views where the key is the name of the view (no spaces) and the value is a dictionary of the options below.

* `jql`: JQL for the view
* `title`: Title to be shown
* `display_flags`: See `ISSUE_DEFAULT_DISPLAY_FLAGS`
* `other_statuses`: See `ISSUE_DEFAULT_OTHER_STATUSES`
* `priority_colors`: See `ISSUE_DEFAULT_PRIORITY_COLORS`
* `status_colors`: See `ISSUE_DEFAULT_STATUS_COLORS`
* `display`: See `ISSUE_DEFAULT_DISPLAY_SETTINGS`

If no default view is specified, a view will be added for open issues of the current user.

Expand All @@ -103,6 +107,7 @@ Defaults to `default`.

## Release History

* `0.7` - Added new component displays; refactored display settings; minor UI tweaks
* `0.6` - Added auto refresh
* `0.5` - Added caching; refactored main view to use APIs
* `0.4` - Added multiple views; various cosmetic changes
Expand Down
24 changes: 20 additions & 4 deletions jira_organizer/app.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from flask import Flask, g
from flask_caching import Cache
from .options import Data

from .options import Data, ComponentDisplay, DisplaySettings
from .jira.client import JiraClient


Expand All @@ -27,9 +28,24 @@
for view_name in list(app.config["ISSUE_VIEWS"].keys()):
view = app.config["ISSUE_VIEWS"][view_name]

for key in ["display_flags", "other_statuses", "status_colors", "priority_colors"]:
if key not in view:
view[key] = app.config[f"ISSUE_DEFAULT_{key.upper()}"]
display_settings = view.get("display", {})
display_defaults = app.config["ISSUE_DEFAULT_DISPLAY_SETTINGS"]

for key in ["flags", "other_statuses"]:
if key not in display_settings:
display_settings[key] = display_defaults.get(key, [])

for key in ["statuses", "priorities", "issue_types"]:
default = display_defaults.get(key, {})

if key not in display_settings:
display_settings[key] = default
else:
for child, value in default.items():
if child not in display_settings[key]:
display_settings[key][child] = value

view["display"] = display_settings

app.config["ISSUE_VIEWS"][view_name] = view

Expand Down
46 changes: 27 additions & 19 deletions jira_organizer/config-sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,33 @@
#
# Organizer

ISSUE_VIEWS = {}

ISSUE_DEFAULT_DISPLAY_FLAGS = [
"show_status",
"show_reporter",
"show_priority"
]
AUTO_REFRESH = 60

ISSUE_DEFAULT_OTHER_STATUSES = []

#
# Display

ISSUE_DEFAULT_STATUS_COLORS = {}
ISSUE_VIEWS = {}

ISSUE_DEFAULT_PRIORITY_COLORS = {
"high": "danger",
"very_high": "danger",
"medium": "warning",
"low": "primary",
"lowest": "primary",
ISSUE_DEFAULT_DISPLAY_SETTINGS = {
"flags": [
"show_status",
"show_reporter",
"show_priority"
],
"other_statuses": [],
"priorities": {
"high": {
"color": "danger",
},
"very_high": {
"color": "danger",
},
"medium": {
"color": "warning",
},
"low": {
"color": "primary",
},
"lowest": {
"color": "primary",
},
}
}

30 changes: 30 additions & 0 deletions jira_organizer/jira/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ def to_dict(self):
return data


class NestedJiraIssue(JiraObject):
field_mapping = {
"jira_id": "id",
"id": "key",
"priority": "fields.priority.name",
"summary": "fields.summary",
"status": "fields.status.name",
"issue_type": "fields.issuetype.name",
}


class JiraIssue(JiraObject):
field_mapping = {
"jira_id": "id",
Expand All @@ -67,12 +78,31 @@ class JiraIssue(JiraObject):
"issue_type": "fields.issuetype.name",
"project": "fields.project.name",
"assignee": "fields.assignee.displayName",
"fix_versions": "fields.fixVersions",
"labels": "fields.labels",
"parent": "fields.parent",
}

@property
def status_slug(self):
return slugify(self.status)

@property
def priority_slug(self):
return slugify(self.priority)

@property
def issue_type_slug(self):
return slugify(self.issue_type)

def update(self, payload=None):
super().update(payload)

self.fix_versions = [d["name"] for d in self.fix_versions]

if hasattr(self, "parent"):
self.parent = NestedJiraIssue(self.parent)


class JiraStatus(JiraObject):
field_mapping = {
Expand Down
60 changes: 60 additions & 0 deletions jira_organizer/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,63 @@ def dump(self):

with open(self.filename, "w+", encoding="utf-8") as fh:
json.dump(payload, fh)


class ComponentDisplay(object):
def __init__(self, slug, display, show_in_main=True, color=None, icon=None):
self.display = display
self.slug = slug
self.show_in_main = show_in_main
self.options = {}
self.defaults = {
"color": color,
"icon": icon
}

def __call__(self, slug):
if slug in self.options:
return self.options[slug]

return self.set(slug)

def set(self, slug, override=True, **options):
for key, value in self.defaults.items():
if key not in options:
options[key] = value

if not override and slug in self.options:
return self.options[slug]

self.options[slug] = options

return self.options[slug]

def build_options(self, view, defaults):
for key, options in view.get(self.slug, {}).items():
self.set(key, **options)

for key, options in defaults.get(self.slug, {}).items():
self.set(key, override=False, **options)


class DisplaySettings(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

defaults = (
("flags", []),
("other_statuses", []),
)

for key, default in defaults:
if key not in self:
self[key] = default

def add_component(self, component):
self[component.slug] = component

def add_flag(self, flag):
self["flags"].append(flag)

def add_other_status(self, slug):
self["other_statuses"].append(slug)
50 changes: 36 additions & 14 deletions jira_organizer/templates/partials/issue.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{% macro show_issue_meta(icon, class=None, sm=False, tooltip=None) %}
{% macro show_issue_meta(icon=None, class=None, sm=False, tooltip=None) %}
<li class="list-inline-item small me-{% if sm %}3{% else %}4{% endif %}{% if class %} {{ class }}{% endif %}"{% if tooltip %} data-bs-toggle="tooltip" data-bs-title="{{ tooltip }}"{% endif %}>
<i class="{{ icon }} pe-1"></i>
{% if icon %}<i class="{{ icon }} pe-1"></i>{% endif %}
{{ caller() }}
</li>
{% endmacro %}

{% if sm %}{% set list_me = 2 %}
{% else %}{% set list_me = 4 %}{% endif %}

<div class="issue-container card shadow mb-3" data-id="{{ issue.jira_id }}" style="border-top: 4px solid {{ status_colors[issue.status]|default('gray') }}">
<div class="issue-container card shadow mb-3 border-top border-{{ display.statuses.get(issue.status_slug, {}).get("color", "secondary") }}" data-id="{{ issue.jira_id }}" style="border-top-width: 4px !important">
<div class="card-body">
{% if issue.is_new %}
<div class="float-end new-issue" data-bs-toggle="tooltip" data-bs-title="New issue">
Expand All @@ -21,41 +21,63 @@
<div class="row mb-0">
<div class="col-10">
<ul class="list-inline list-unstyled mb-0">
{% call show_issue_meta(2, sm=sm, tooltip="Jira Issue ID") %}
{% call show_issue_meta(sm=sm, tooltip="Jira Issue ID") %}
<a href="https://{{ config['JIRA_SUBDOMAIN'] }}.atlassian.net/browse/{{ issue.id }}" target="_blank">
{{ issue.id }}
</a>
{% endcall %}
{% if 'show_assignee' in display_flags and not sm %}
{% if 'show_issue_type' in display.flags %}
{% call show_issue_meta(display.issue_types.get(issue.issue_type_slug, {}).get("icon", "fas fa-dot-circle"), sm=sm, tooltip="Issue Type") %}
{{ issue.issue_type }}
{% endcall %}
{% endif %}
{% if 'show_assignee' in display.flags and not sm %}
{% call show_issue_meta("fas fa-user-ninja", sm=sm, tooltip="Current Assignee") %}
{% if issue.assignee %}{{ issue.assignee }}{% else %}Unassigned{% endif %}
{% endcall %}
{% endif %}
{% if 'show_status' in display_flags %}
{% call show_issue_meta("far fa-bell", sm=sm, tooltip="Issue Status") %}
{% if 'show_status' in display.flags %}
{% if issue.status_slug in display.statuses %}
{% set class_name="text-" + display.statuses.get(issue.status_slug).get("color") %}
{% else %}{% set class_name = None %}{% endif %}
{% call show_issue_meta("far fa-bell", sm=sm, class=class_name, tooltip="Issue Status") %}
{{ issue.status }}
{% endcall %}
{% endif %}
{% if not sm %}
{% if 'show_project' in display_flags %}
{% if 'show_project' in display.flags %}
{% call show_issue_meta("fas fa-bookmark", sm=sm, tooltip="Jira Project") %}
{{ issue.project }}
{% endcall %}
{% endif %}
{% if 'show_reporter' in display_flags %}
{% if 'show_reporter' in display.flags %}
{% call show_issue_meta("fas fa-user", sm=sm, tooltip="Reporter") %}
{{ issue.reporter }}
{% endcall %}
{% endif %}
{% if 'show_priority' in display_flags %}
{% if issue.priority_slug in priority_colors %}
{% set class_name="text-" + priority_colors[issue.priority_slug] %}
{% else %}{% set class_name = None %}
{% endif %}
{% if 'show_priority' in display.flags %}
{% if issue.priority_slug in display.priorities %}
{% set class_name="text-" + display.priorities.get(issue.priority_slug).get("color") %}
{% else %}{% set class_name = None %}{% endif %}
{% call show_issue_meta("fas fa-exclamation-triangle", sm=sm, class=class_name, tooltip="Priority") %}
{{ issue.priority }}
{% endcall %}
{% endif %}
{% if 'show_version' in display.flags and issue.fix_versions|length %}
{% call show_issue_meta("fas fa-code-branch", sm=sm, tooltip="Version") %}
{{ issue.fix_versions | join(', ') }}
{% endcall %}
{% endif %}
{% if 'show_labels' in display.flags and issue.labels|length %}
{% call show_issue_meta("fas fa-tags", sm=sm, tooltip="Labels") %}
{{ issue.labels | join(', ') }}
{% endcall %}
{% endif %}
{% if 'show_parent' in display.flags and issue.parent %}
{% call show_issue_meta("fas fa-sitemap", sm=sm, tooltip="Parent") %}
{{ issue.parent.summary }}
{% endcall %}
{% endif %}
{% endif %}
</ul><!-- ul -->
</div><!-- .col -->
Expand Down
2 changes: 1 addition & 1 deletion jira_organizer/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def _get_issues(view_name, force=False):
sorted_ids.append(issue.jira_id)
continue

if slugify(issue.status) in app.config["ISSUE_DEFAULT_OTHER_STATUSES"]:
if slugify(issue.status) in view["display"]["other_statuses"]:
columns["other"].append(issue)
sorted_ids.append(issue.jira_id)
continue
Expand Down

0 comments on commit 5ce29e4

Please sign in to comment.