From 13f756874aa56c7fc0aad2d2d996f790928c87b4 Mon Sep 17 00:00:00 2001 From: Marlen Brunner Date: Wed, 2 Oct 2024 14:33:58 -0700 Subject: [PATCH 1/4] :bug: Fix broken styling of tooltip notifier of estimated values in reconciliation page. Issue primarily was that parent component was missing possition: relative. The rest of the fixes, are me taking advantage of better Vuetify 3 options. --- ...ntreDashboardSummaryReconciliationPage.vue | 69 ++++++++----------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/web/src/modules/centre/pages/CentreDashboardSummaryReconciliationPage.vue b/web/src/modules/centre/pages/CentreDashboardSummaryReconciliationPage.vue index 6850206a..fa841e69 100644 --- a/web/src/modules/centre/pages/CentreDashboardSummaryReconciliationPage.vue +++ b/web/src/modules/centre/pages/CentreDashboardSummaryReconciliationPage.vue @@ -30,39 +30,33 @@ {{ formatMoney(centsToDollars(payment.amountInCents)) }} - - - - This expense includes estimated values. - - {{ formatMoney(centsToDollars(expense.amountInCents)) }} + + {{ formatMoney(centsToDollars(expense.amountInCents)) }} + - - - - This amount includes estimated values. - - {{ formatMoney(centsToDollars(employee.amountInCents)) }} + + {{ formatMoney(centsToDollars(employee.amountInCents)) }} + @@ -315,9 +309,6 @@ async function buildExpenseValues(fiscalPeriods: FiscalPeriod[]): Promise From 46996bd73a10c61e6af3205f671bfbd1dd7c8ae0 Mon Sep 17 00:00:00 2001 From: Marlen Brunner Date: Wed, 2 Oct 2024 15:19:26 -0700 Subject: [PATCH 4/4] :hammer: Add dev helper to get Jira issue description in markdown format. --- bin/dev | 13 ++++ bin/jira_api.rb | 34 ++++++++-- bin/prose_mirror_to_json.rb | 128 ++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 bin/prose_mirror_to_json.rb diff --git a/bin/dev b/bin/dev index 1897b0fb..c7964a90 100755 --- a/bin/dev +++ b/bin/dev @@ -7,6 +7,7 @@ class DevHelper COMMAND_TO_METHOD = { "ts-node" => :ts_node, "branch-from" => :branch_from, + "description-from" => :description_from, } METHOD_TO_COMMAND = COMMAND_TO_METHOD.invert @@ -189,6 +190,18 @@ class DevHelper system("git checkout -b #{branch_name}") end + ## + # Fetches the description of a Jira issue and prints it to the console in markdown format. + # Example: + # dev description-from https://yg-hpw.atlassian.net/browse/ELCC-61 + # + # Produces: + # ... a bunch of markdown text ... + def description_from(jira_issue_url, *args, **kwargs) + description = JiraApi.fetch_description_as_markdown(jira_issue_url) + puts description + end + def bash_completions completions = public_methods(false).reject { |word| %i[call].include?(word) diff --git a/bin/jira_api.rb b/bin/jira_api.rb index 77493dcb..68c61eed 100644 --- a/bin/jira_api.rb +++ b/bin/jira_api.rb @@ -2,6 +2,8 @@ require 'json' require 'uri' +require_relative "./prose_mirror_to_json.rb" + class JiraApi JIRA_USERNAME = ENV['JIRA_USERNAME'] JIRA_API_TOKEN = ENV['JIRA_API_TOKEN'] @@ -14,17 +16,30 @@ def self.build_branch_name(jira_ticket_url) end issue_key = extract_issue_key(jira_ticket_url) - issue_title = fetch_issue_title(issue_key) + issue_data = fetch_issue_data(issue_key) + issue_title = extract_issue_title(issue_data) format_branch_name(issue_key, issue_title) end + def self.fetch_description_as_markdown(jira_ticket_url) + if JIRA_USERNAME.nil? || JIRA_API_TOKEN.nil? + puts 'Please set JIRA_USERNAME and JIRA_API_TOKEN environment variables' + return + end + + issue_key = extract_issue_key(jira_ticket_url) + issue_data = fetch_issue_data(issue_key) + issue_description = extract_issue_description(issue_data) + prose_mirror_to_markdown(issue_description) + end + private def self.extract_issue_key(url) url.match(%r{/browse/([A-Z]+-\d+)})[1] end - def self.fetch_issue_title(issue_key) + def self.fetch_issue_data(issue_key) uri = URI("#{JIRA_SITE}/rest/api/3/issue/#{issue_key}") request = Net::HTTP::Get.new(uri) request.basic_auth(JIRA_USERNAME, JIRA_API_TOKEN) @@ -33,12 +48,23 @@ def self.fetch_issue_title(issue_key) http.request(request) end - data = JSON.parse(response.body) - data['fields']['summary'] + JSON.parse(response.body) + end + + def self.extract_issue_title(issue_data) + issue_data['fields']['summary'] + end + + def self.extract_issue_description(issue_data) + issue_data['fields']['description'] end def self.format_branch_name(issue_key, issue_title) formatted_title = issue_title.downcase.gsub(/\s+/, '-').gsub(/[^a-z0-9\-]/, '') "#{issue_key.downcase}/#{formatted_title}" end + + def self.prose_mirror_to_markdown(description) + ProseMirrorToMarkdown.call(description) + end end diff --git a/bin/prose_mirror_to_json.rb b/bin/prose_mirror_to_json.rb new file mode 100644 index 00000000..5f62e4a9 --- /dev/null +++ b/bin/prose_mirror_to_json.rb @@ -0,0 +1,128 @@ +require 'json' + +## +# Example usage: +# +# Suppose 'prosemirror_json' is the JSON data you've provided. +# +# Load the JSON data +# prosemirror_json = DownloadJiraDescription.call(jira_ticket_url) +# +# markdown = ProseMirrorToMarkdown.call(prosemirror_json) +# puts markdown +class ProseMirrorToMarkdown + def self.call(prosemirror_json) + new(prosemirror_json).to_markdown + end + + def initialize(prosemirror_json) + @doc = prosemirror_json + end + + def to_markdown + process_nodes(@doc['content']).join("\n") + end + + private + + def process_nodes(nodes) + nodes.flat_map { |node| process_node(node) } + end + + def process_node(node) + case node['type'] + when 'doc' + process_nodes(node['content']) + when 'paragraph' + process_paragraph(node) + when 'heading' + process_heading(node) + when 'blockquote' + process_blockquote(node) + when 'orderedList' + process_ordered_list(node) + when 'bulletList' + process_bullet_list(node) + when 'listItem' + process_list_item(node) + when 'text' + process_text(node) + else + # For any other nodes, process their content if they have any + if node['content'] + process_nodes(node['content']) + else + [] + end + end + end + + def process_paragraph(node) + [process_inline_content(node['content'])] + end + + def process_heading(node) + level = node['attrs']['level'] + content = process_inline_content(node['content']) + ["\n#{'#' * level} #{content}\n"] + end + + def process_blockquote(node) + content = process_nodes(node['content']).map { |line| "> #{line}" }.join("\n") + ["#{content}\n"] + end + + def process_ordered_list(node) + start_number = node['attrs']['order'] || 1 + items = node['content'] + process_list_items(items, ordered: true, start_number: start_number) + end + + def process_bullet_list(node) + items = node['content'] + process_list_items(items, ordered: false) + end + + def process_list_items(items, ordered:, start_number: 1) + counter = start_number + items.flat_map do |item| + lines = process_nodes(item['content']) + prefix = ordered ? "#{counter}. " : "- " + counter += 1 + lines.map.with_index do |line, idx| + if idx == 0 + "#{prefix}#{line}" + else + " #{line}" + end + end + end + end + + def process_text(node) + text = node['text'] + if node['marks'] + node['marks'].each do |mark| + case mark['type'] + when 'bold' + text = "**#{text}**" + when 'italic' + text = "*#{text}*" + when 'code' + text = "`#{text}`" + when 'link' + href = mark['attrs']['href'] + text = "[#{text}](#{href})" + when 'strike' + text = "~~#{text}~~" + # Add more mark types as needed + end + end + end + text + end + + def process_inline_content(content) + content.map { |node| process_node(node) }.join + end +end