diff --git a/app/services/repository_zip_export.rb b/app/services/repository_zip_export.rb index 7ca6cda00..cf62b6b03 100644 --- a/app/services/repository_zip_export.rb +++ b/app/services/repository_zip_export.rb @@ -11,12 +11,12 @@ module RepositoryZipExport zip = ZipExport.create(user: current_user) zip.generate_exportable_zip( current_user, - to_csv(ordered_rows, params[:header_ids]), + to_csv(ordered_rows, params[:header_ids], current_user, repository.team), :repositories ) end - def self.to_csv(rows, column_ids) + def self.to_csv(rows, column_ids, user, team) # Parse column names csv_header = [] column_ids.each do |c_id| @@ -56,7 +56,11 @@ module RepositoryZipExport else cell = row.repository_cells .find_by(repository_column_id: c_id) - cell ? cell.value.formatted : nil + if cell + SmartAnnotations::TagToText.new( + user, team, cell.value.formatted + ).text + end end end csv << csv_row diff --git a/app/services/smart_annotations/preview.rb b/app/services/smart_annotations/html_preview.rb similarity index 99% rename from app/services/smart_annotations/preview.rb rename to app/services/smart_annotations/html_preview.rb index 33765dae3..39ae0e47e 100644 --- a/app/services/smart_annotations/preview.rb +++ b/app/services/smart_annotations/html_preview.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module SmartAnnotations - class Preview + class HtmlPreview class << self def html(name, type, object) send("generate_#{type}_snippet", name, object) diff --git a/app/services/smart_annotations/tag_to_html.rb b/app/services/smart_annotations/tag_to_html.rb index 0e4741b4c..f782e0022 100644 --- a/app/services/smart_annotations/tag_to_html.rb +++ b/app/services/smart_annotations/tag_to_html.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'smart_annotations/permision_eval' -require 'smart_annotations/preview' +require 'smart_annotations/html_preview' module SmartAnnotations class TagToHtml @@ -32,7 +32,7 @@ module SmartAnnotations next unless object && SmartAnnotations::PermissionEval.check(user, type, object) - SmartAnnotations::Preview.html(nil, type, object) + SmartAnnotations::HtmlPreview.html(nil, type, object) end rescue ActiveRecord::RecordNotFound next @@ -42,9 +42,9 @@ module SmartAnnotations def repository_item(name, user, type, object) if object && SmartAnnotations::PermissionEval.check(user, type, object) - return SmartAnnotations::Preview.html(nil, type, object) + return SmartAnnotations::HtmlPreview.html(nil, type, object) end - SmartAnnotations::Preview.html(name, type, object) + SmartAnnotations::HtmlPreview.html(name, type, object) end def extract_values(element) diff --git a/app/services/smart_annotations/tag_to_text.rb b/app/services/smart_annotations/tag_to_text.rb new file mode 100644 index 000000000..f197338d1 --- /dev/null +++ b/app/services/smart_annotations/tag_to_text.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'smart_annotations/permision_eval' +require 'smart_annotations/text_preview' + +module SmartAnnotations + class TagToText + attr_reader :text + + def initialize(user, team, text) + parse_items_annotations(user, text) + parse_users_annotations(user, team, @text) + end + + private + + USER_REGEX = /\[\@(.*?)~([0-9a-zA-Z]+)\]/ + ITEMS_REGEX = /\[\#(.*?)~(prj|exp|tsk|rep_item)~([0-9a-zA-Z]+)\]/ + OBJECT_MAPPINGS = { prj: Project, + exp: Experiment, + tsk: MyModule, + rep_item: RepositoryRow }.freeze + + def parse_items_annotations(user, text) + @text = text.gsub(ITEMS_REGEX) do |el| + value = extract_values(el) + type = value[:object_type] + begin + object = fetch_object(type, value[:object_id]) + # handle repository_items edge case + if type == 'rep_item' + repository_item(value[:name], user, type, object) + else + next unless object && SmartAnnotations::PermissionEval.check(user, + type, + object) + SmartAnnotations::TextPreview.text(nil, type, object) + end + rescue ActiveRecord::RecordNotFound + next + end + end + end + + def parse_users_annotations(user, team, text) + @text = text.gsub(USER_REGEX) do |el| + match = el.match(USER_REGEX) + user = User.find_by_id(match[2].base62_decode) + next unless user + next if UserTeam.where(user: user, team: team).empty? + user.full_name + end + end + + def repository_item(name, user, type, object) + if object && SmartAnnotations::PermissionEval.check(user, type, object) + return SmartAnnotations::TextPreview.text(nil, type, object) + end + SmartAnnotations::TextPreview.text(name, type, object) + end + + def extract_values(element) + match = element.match(ITEMS_REGEX) + { + name: match[1], + object_type: match[2], + object_id: match[3].base62_decode + } + end + + def fetch_object(type, id) + klass = OBJECT_MAPPINGS.fetch(type.to_sym) do + raise ActiveRecord::RecordNotFound.new("#{type} does not exist") + end + klass.find_by_id(id) + end + end +end diff --git a/app/services/smart_annotations/text_preview.rb b/app/services/smart_annotations/text_preview.rb new file mode 100644 index 000000000..505c9ebee --- /dev/null +++ b/app/services/smart_annotations/text_preview.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module SmartAnnotations + class TextPreview + class << self + def text(name, type, object) + send("generate_#{type}_snippet", name, object) + end + + private + + ROUTES = Rails.application.routes.url_helpers + + def generate_prj_snippet(_, object) + if object.archived? + return "#{object.name} #{I18n.t('atwho.res.archived')}" + end + object.name + end + + def generate_exp_snippet(_, object) + if object.archived? + return "#{object.name} #{I18n.t('atwho.res.archived')}" + end + object.name + end + + def generate_tsk_snippet(_, object) + if object.archived? + return "#{object.name} #{I18n.t('atwho.res.archived')}" + end + object.name + end + + def generate_rep_item_snippet(name, object) + if object + return object.name + end + "#{name} #{I18n.t('atwho.res.deleted')}" + end + end + end +end diff --git a/spec/services/smart_annotations/preview_spec.rb b/spec/services/smart_annotations/html_preview_spec.rb similarity index 95% rename from spec/services/smart_annotations/preview_spec.rb rename to spec/services/smart_annotations/html_preview_spec.rb index 45d836c14..8b2f4f9cc 100644 --- a/spec/services/smart_annotations/preview_spec.rb +++ b/spec/services/smart_annotations/html_preview_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' -require 'smart_annotations/preview' +require 'smart_annotations/html_preview' -describe SmartAnnotations::Preview do +describe SmartAnnotations::HtmlPreview do let(:subject) { described_class } let(:user) { create :user } let(:project) { create :project, name: 'my project' } diff --git a/spec/services/smart_annotations/tag_to_text_spec.rb b/spec/services/smart_annotations/tag_to_text_spec.rb new file mode 100644 index 000000000..76d453dd1 --- /dev/null +++ b/spec/services/smart_annotations/tag_to_text_spec.rb @@ -0,0 +1,58 @@ +require 'rails_helper' + +describe SmartAnnotations::TagToText do + let!(:user) { create :user } + let!(:team) { create :team } + let!(:user_team) { create :user_team, user: user, team: team, role: 2 } + let!(:project) { create :project, name: 'my project', team: team } + let!(:user_project) do + create :user_project, project: project, user: user, role: 0 + end + let(:text) do + "My annotation of [#my project~prj~#{project.id.base62_encode}]" + end + let(:subject) { described_class.new(user, team, text) } + describe 'Parsed text' do + it 'returns a existing string with smart annotation' do + expect(subject.text).to eq("My annotation of #{project.name}") + end + end + + describe '#extract_values/1' do + it 'returns a parsed hash of smart annotation' do + values = subject.send(:extract_values, '[#my project~prj~1]') + expect(values[:name]).to eq 'my project' + expect(values[:object_id]).to eq 1 + expect(values[:object_type]).to eq 'prj' + end + end + + describe '#fetch_object/2' do + it 'rises an error if type is not valid' do + expect { + subject.send(:fetch_object, 'banana', project.id) + }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'returns the required object' do + expect(subject.send(:fetch_object, 'prj', project.id)).to eq project + end + end + + describe '#parse_users_annotations/2' do + let!(:user_two) { create :user, email: 'test_user@asdf.com' } + let(:text) { "[@#{user.full_name}~#{user.id.base62_encode}]" } + it 'parses the user tokent to text format' do + expect( + subject.send(:parse_users_annotations, user, team, text) + ).to eq user.full_name + end + + it 'trims the user annotation if user is not member of a team' do + random_text = "Sec:[@#{user_two.full_name}~#{user_two.id.base62_encode}]" + expect( + subject.send(:parse_users_annotations, user, team, random_text) + ).to eq "Sec:" + end + end +end diff --git a/spec/services/smart_annotations/text_preview_spec.rb b/spec/services/smart_annotations/text_preview_spec.rb new file mode 100644 index 000000000..42e9ade26 --- /dev/null +++ b/spec/services/smart_annotations/text_preview_spec.rb @@ -0,0 +1,43 @@ +require 'rails_helper' +require 'smart_annotations/text_preview' + +describe SmartAnnotations::TextPreview do + let(:subject) { described_class } + let(:user) { create :user } + let(:project) { create :project, name: 'my project' } + let(:experiment) do + create :experiment, name: 'my experiment', + project: project, + created_by: user, + last_modified_by: user + end + let(:task) { create :my_module, name: 'task', experiment: experiment } + + describe 'Project annotations' do + it 'returns a text snippet' do + snippet = subject.text(nil, 'prj', project) + expect(snippet).to eq(project.name) + end + end + + context 'Experiment annotations' do + it 'returns a text snippet' do + snippet = subject.text(nil, 'exp', experiment) + expect(snippet).to eq(experiment.name) + end + end + + context 'MyModule annotations' do + it 'returns a text snippet' do + snippet = subject.text(nil, 'tsk', task) + expect(snippet).to eq(task.name) + end + end + + context 'Repository item annotations with type rep_item' do + it 'returns a html snippet' do + snippet = subject.text('my item', 'rep_item', nil) + expect(snippet).to eq('my item (deleted)') + end + end +end