diff --git a/README.md b/README.md index 53751ff..91865c5 100644 --- a/README.md +++ b/README.md @@ -42,37 +42,44 @@ 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` @@ -80,10 +87,7 @@ Dictionary of views where the key is the name of the view (no spaces) and the va * `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. @@ -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 diff --git a/jira_organizer/app.py b/jira_organizer/app.py index d3bbd7e..6fcb3f2 100644 --- a/jira_organizer/app.py +++ b/jira_organizer/app.py @@ -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 @@ -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 diff --git a/jira_organizer/config-sample.py b/jira_organizer/config-sample.py index 5c1eb6f..e704717 100644 --- a/jira_organizer/config-sample.py +++ b/jira_organizer/config-sample.py @@ -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", + }, + } } + diff --git a/jira_organizer/jira/models.py b/jira_organizer/jira/models.py index e870ac8..2186d9b 100644 --- a/jira_organizer/jira/models.py +++ b/jira_organizer/jira/models.py @@ -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", @@ -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 = { diff --git a/jira_organizer/options.py b/jira_organizer/options.py index 1e0cda2..cc24f19 100644 --- a/jira_organizer/options.py +++ b/jira_organizer/options.py @@ -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) diff --git a/jira_organizer/templates/partials/issue.html b/jira_organizer/templates/partials/issue.html index de9726f..23dcf49 100644 --- a/jira_organizer/templates/partials/issue.html +++ b/jira_organizer/templates/partials/issue.html @@ -1,6 +1,6 @@ -{% macro show_issue_meta(icon, class=None, sm=False, tooltip=None) %} +{% macro show_issue_meta(icon=None, class=None, sm=False, tooltip=None) %}