1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-20 14:54:15 +01:00

projects to markdown in zip

This commit is contained in:
Nicolas Florentin 2023-06-30 11:15:37 +02:00
parent 82823dd4cc
commit 50ed3c9ed2
18 changed files with 120 additions and 21 deletions

View File

@ -30,6 +30,7 @@ group :development, :test do
# comment over to use visual debugger (eg. RubyMine), uncomment to use manual debugging
# gem 'byebug'
gem 'dotenv-rails'
gem 'pry'
end
group :development do
@ -43,7 +44,6 @@ group :development do
# Preview mail in the browser
gem 'listen', '~> 3.0.5'
gem 'overcommit'
gem 'pry'
gem 'rb-readline'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'railroady'
@ -150,4 +150,4 @@ gem 'acts_as_list'
gem 'sentry-rails'
gem 'sentry-ruby'
gem "reverse_markdown"
gem "reverse_markdown"

View File

@ -59,12 +59,23 @@ class API::ProjectsController < API::APIController
def search
service = ProjectService.new
res = service.search(params, current_user)
paginate = request.format.zip? ? false : true
res = service.search(params, current_user, paginate: paginate)
render json: res, status: :unprocessable_entity and return if res[:error]
@total = res[:total]
@projects = res[:projects]
render :index
respond_to do |format|
format.json do
@total = res[:total]
@projects = res[:projects]
render :index
end
format.zip do
head :forbidden unless current_user.admin? || current_user.manager?
send_data ProjectsArchive.new(res[:projects]).call, filename: "projets.zip", disposition: 'attachment', type: 'application/zip'
end
end
end
private

View File

@ -332,6 +332,8 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
$scope.triggerSearch();
};
$scope.zipUrl = '/api/projects/search.zip';
/**
* Callback triggered when the button "search from the whole network" is toggled
*/
@ -420,6 +422,9 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
updateUrlParam('component_id', search.component_id);
updateUrlParam('machine_id', search.machine_id);
updateUrlParam('status_id', search.status_id);
$scope.zipUrl = '/api/projects/search.zip?' + new URLSearchParams({ search: JSON.stringify($location.search()) }).toString();
return true;
};

View File

@ -67,6 +67,12 @@
<status-filter on-filter-change="onStatusChange" current-status-index="search.status_id"/>
</div>
<div class="text-center m m-b-lg" ng-if="!openlab.searchOverWholeNetwork && (projects.length != 0) && (isAuthorized('admin') || isAuthorized('manager'))">
<a class="btn bg-light text-black" ng-href="{{ zipUrl }}" target="_blank">
<i class="fa fa-download"></i> {{ 'app.public.projects_list.download_archive' | translate }}
</a>
</div>
</div>
<div class="projects-list">

View File

@ -174,8 +174,8 @@
</div>
</section>
<div class="text-center m m-b-lg" ng-if="projectEditableBy(currentUser) || isAuthorized('admin')">
<a class="btn btn-default" ng-href="api/projects/{{ project.id}}/markdown" target="_blank">
<div class="text-center m m-b-lg" ng-if="projectEditableBy(currentUser) || isAuthorized('admin') || isAuthorized('manager')">
<a class="btn bg-light text-black" ng-href="api/projects/{{ project.id}}/markdown" target="_blank">
<i class="fa fa-download"></i> {{ 'app.public.projects_show.markdown_file' | translate }}
</a>
</div>

View File

@ -5,4 +5,6 @@ class ProjectStep < ApplicationRecord
belongs_to :project, touch: true
has_many :project_step_images, as: :viewable, dependent: :destroy
accepts_nested_attributes_for :project_step_images, allow_destroy: true, reject_if: :all_blank
default_scope -> { order(:step_nb) }
end

View File

@ -16,14 +16,14 @@ class ProjectPolicy < ApplicationPolicy
end
def update?
user.admin? or record.author.user_id == user.id or record.users.include?(user)
user.admin? || record.author.user_id == user.id || record.users.include?(user)
end
def markdown?
user.admin? or record.author.user_id == user.id or record.users.include?(user)
user.admin? || user.manager? || record.author.user_id == user.id || record.users.include?(user)
end
def destroy?
user.admin? or record.author.user_id == user.id
user.admin? || record.author.user_id == user.id
end
end

View File

@ -2,17 +2,17 @@
# Provides methods for Project
class ProjectService
def search(params, current_user)
def search(params, current_user, paginate: true)
connection = ActiveRecord::Base.connection
return { error: 'invalid adapter' } unless connection.instance_values['config'][:adapter] == 'postgresql'
search_from_postgre(params, current_user)
search_from_postgre(params, current_user, paginate: paginate)
end
private
def search_from_postgre(params, current_user)
query_params = JSON.parse(params[:search])
def search_from_postgre(params, current_user, paginate: true)
query_params = JSON.parse(params[:search] || "{}")
records = Project.published_or_drafts(current_user&.statistic_profile&.id)
records = Project.user_projects(current_user&.statistic_profile&.id) if query_params['from'] == 'mine'
@ -29,6 +29,9 @@ class ProjectService
records.order(created_at: :desc)
end
{ total: records.count, projects: records.includes(:users, :project_image).page(params[:page]) }
records = records.includes(:users, :project_image)
records = records.page(params[:page]) if paginate
{ total: records.count, projects: records }
end
end

View File

@ -14,7 +14,7 @@ class ProjectToMarkdown
md << ReverseMarkdown.convert(project.description.to_s)
project_steps = project.project_steps.order(:step_nb)
project_steps = project.project_steps
if project_steps.present?
md << "## #{I18n.t('app.shared.project.steps')}"
@ -29,6 +29,9 @@ class ProjectToMarkdown
end
end
md << "## #{I18n.t('app.shared.project.author')}"
md << project.author&.user&.profile&.full_name
if project.themes.present?
md << "## #{I18n.t('app.shared.project.themes')}"
md << project.themes.map(&:name).join(', ')
@ -41,8 +44,10 @@ class ProjectToMarkdown
end
end
md << "## #{I18n.t('app.shared.project.status')}"
md << project.status.name
if project.status
md << "## #{I18n.t('app.shared.project.status')}"
md << project.status.name
end
if project.machines.present?
md << "## #{I18n.t('app.shared.project.employed_machines')}"
@ -54,9 +59,9 @@ class ProjectToMarkdown
md << project.components.map(&:name).join(', ')
end
if project.project_users.present?
if project.users.present?
md << "## #{I18n.t('app.shared.project.collaborators')}"
md << project.project_users.map { |pu| pu.user.profile.full_name }.join(', ')
md << project.users.map { |u| u.profile.full_name }.join(', ')
end
if project.licence.present?

View File

@ -0,0 +1,22 @@
class ProjectsArchive
attr_reader :projects
def initialize(projects)
@projects = projects
end
def call
stringio = Zip::OutputStream.write_buffer do |zio|
projects.includes(:project_image, :themes,
:project_caos, :status, :machines,
:components, :licence,
project_steps: :project_step_images,
author: { user: :profile },
users: :profile).find_each do |project|
zio.put_next_entry("#{project.name.parameterize}-#{project.id}.md")
zio.write ProjectToMarkdown.new(project).call
end
end
stringio.string
end
end

View File

@ -183,6 +183,7 @@ en:
all_materials: "All materials"
load_next_projects: "Load next projects"
rough_draft: "Rough draft"
download_archive: Download
status_filter:
all_statuses: "All statuses"
select_status: "Select a status"

View File

@ -183,6 +183,7 @@ fr:
all_materials: "Tous les matériaux"
load_next_projects: "Charger les projets suivants"
rough_draft: "Brouillon"
download_archive: Télécharger
status_filter:
all_statuses: "Tous les statuts"
select_status: "Sélectionnez un statut"

View File

@ -151,6 +151,7 @@ en:
employed_materials: "Employed materials"
employed_machines: "Employed machines"
collaborators: "Collaborators"
author: Author
creative_commons_licences: "Creative Commons licences"
licence: "Licence"
themes: "Themes"

View File

@ -151,6 +151,7 @@ fr:
employed_materials: "Matériaux utilisés"
employed_machines: "Machines utilisées"
collaborators: "Les collaborateurs"
author: Auteur
creative_commons_licences: "Licences Creative Commons"
licence: "Licence"
themes: "Thématiques"

View File

@ -7,6 +7,7 @@ project_step_1:
created_at: 2016-04-04 15:39:08.259759000 Z
updated_at: 2016-04-04 15:39:08.259759000 Z
title: Le manche
step_nb: 1
project_step_2:
id: 2
@ -16,3 +17,4 @@ project_step_2:
created_at: 2016-04-04 15:39:08.265840000 Z
updated_at: 2016-04-04 15:39:08.265840000 Z
title: La presse
step_nb: 2

View File

@ -12,3 +12,4 @@ project_1:
state: published
slug: presse-puree
published_at: 2016-04-04 15:39:08.267614000 Z
status_id: 1

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
require 'test_helper'
class ProjectsTest < ActionDispatch::IntegrationTest
def setup
@admin = User.find_by(username: 'admin')
login_as(@admin, scope: :user)
end
test 'download markdown file' do
get "/api/projects/1/markdown"
assert_response :success
assert_equal "text/markdown", response.content_type
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'test_helper'
class ProjectToMarkdownTest < ActiveSupport::TestCase
test "ProjectToMarkdown is working" do
project = projects(:project_1)
service = ProjectToMarkdown.new(project)
markdown_str = nil
assert_nothing_raised do
markdown_str = service.call
end
assert_includes markdown_str, project.name
project.project_steps.each do |project_step|
assert_includes markdown_str, project_step.title
end
end
end