Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5b9c65d
feat: get authors from application proposals
Stef-Rousset Sep 12, 2025
7a68c82
feat: get containers from application proposals
Stef-Rousset Sep 12, 2025
1117dbe
feat: get contributions from application proposals
Stef-Rousset Sep 12, 2025
bff7703
feat: get data from application proposals
Stef-Rousset Sep 12, 2025
a3fbcd0
test: update model and controller tests
Stef-Rousset Sep 12, 2025
785fb26
feat: filter retrieved proposals
Stef-Rousset Oct 3, 2025
09db723
feat: add service to get data from api
Stef-Rousset Oct 3, 2025
25c54c8
test: add tests for service
Stef-Rousset Oct 3, 2025
e1e1325
feat: add proposals index extends to display external proposals
Stef-Rousset Oct 3, 2025
03b7f3f
feat: add new sttings to proposal component
Stef-Rousset Oct 3, 2025
64ed913
feat: add views and js file
Stef-Rousset Oct 3, 2025
2e204bf
feat: add translations keys for new settings
Stef-Rousset Oct 3, 2025
99e205f
test: add controller and system tests for proposals index
Stef-Rousset Oct 3, 2025
b67c595
ci: add imagemagick
Stef-Rousset Oct 3, 2025
4c4f2cc
chore: update version of uri
Stef-Rousset Oct 17, 2025
7e3c3dc
feat: update service and api controllers for escaping ref and adding …
Stef-Rousset Oct 17, 2025
7b8c5da
feat: add new setting to choose preferred locale
Stef-Rousset Oct 17, 2025
1c9bbc3
feat: update models for preferred locale
Stef-Rousset Oct 17, 2025
6dc6f7c
feat: update en file and add fr file
Stef-Rousset Oct 17, 2025
e61bd4a
feat: update external_proposal view and add style
Stef-Rousset Oct 17, 2025
118c9f0
feat: update proposal controller
Stef-Rousset Oct 17, 2025
82d67c9
test: update tests
Stef-Rousset Oct 17, 2025
b8a6cbe
refactor: with rubocop
Stef-Rousset Oct 17, 2025
79a6a1e
feat: show/hide preferred locale and add placeholder for url
Stef-Rousset Oct 17, 2025
b40f512
fix: remove unused key in locales files
Stef-Rousset Oct 17, 2025
563fee2
feat: add external_proposal show and add comments
Stef-Rousset Oct 28, 2025
bab5670
feat: add view for extenral_proposal and new partials for comments
Stef-Rousset Oct 28, 2025
5e1b258
test: add tests for external_proposal and update tests with comments
Stef-Rousset Oct 28, 2025
4b4cd22
chore: update readme
Stef-Rousset Oct 28, 2025
80359fc
feat: add proposal state in metadata
Stef-Rousset Oct 29, 2025
8e9b577
feat: add helper and update views to display state
Stef-Rousset Oct 29, 2025
9f368df
test: add tests for helper
Stef-Rousset Oct 29, 2025
c4fa3b1
test: update tests
Stef-Rousset Oct 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci_dataspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ jobs:
name: Create test app
- run: mkdir -p ./spec/decidim_dummy_app/tmp/screenshots
name: Create the screenshots folder
- run: |
sudo apt update
sudo apt install libu2f-udev imagemagick
name: install needed gems
- uses: nanasess/setup-chromedriver@v2
- run: RAILS_ENV=test bundle exec rails shakapacker:compile
name: Precompile assets
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ gem "decidim-dataspace", path: "."

gem "bootsnap", "~> 1.4"
gem "puma", ">= 6.3.1"
gem "uri", ">= 1.0.4"

group :development, :test do
gem "byebug", "~> 11.0", platform: :mri
Expand Down
3 changes: 2 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ GEM
uber (0.1.0)
unicode-display_width (2.6.0)
uniform_notifier (1.17.0)
uri (1.0.3)
uri (1.0.4)
valid_email2 (4.0.6)
activemodel (>= 3.2)
mail (~> 2.5)
Expand Down Expand Up @@ -826,6 +826,7 @@ DEPENDENCIES
letter_opener_web (~> 2.0)
listen (~> 3.1)
puma (>= 6.3.1)
uri (>= 1.0.4)
web-console (~> 4.2)

RUBY VERSION
Expand Down
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,25 @@ bundle exec rake db:migrate

## API endpoints
+ Retrieve all data from the data space\
GET "/data"
GET "api/v1/data"
+ Retrieve all containers from the data space\
GET "/data/containers"
GET "api/v1/data/containers"
+ Retrieve a container using its reference\
GET "/data/containers/:reference"
GET "api/v1/data/containers/:reference"
+ Retrieve all contributions from the data space\
GET "/data/contributions"
GET "api/v1/data/contributions"
+ Retrieve a contribution using its reference\
GET "/data/contributions/:reference"
GET "api/v1/data/contributions/:reference"
+ Retrieve all authors from the data space\
GET "/data/authors"
GET "api/v1/data/authors"
+ Retrieve an author using its reference\
GET "/data/authors/:reference"
GET "api/v1/data/authors/:reference"

Please note that for the 2 endpoints related to contribution, you can add 2 query params
+ "preferred_locale=fr" to get the data with your favorite language (default is "en")
+ "with_comments=true" (default is false)
+ for contributions endpoint, it will give you proposals and comments (the default is only proposals)
+ for contribution endpoint, it will give you a proposal with detailed comments as children

Please note that the reference is the last part of the URL and **needs to be URL encoded**

Expand Down
25 changes: 9 additions & 16 deletions app/controllers/decidim/dataspace/api/v1/authors_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,21 @@ class Api::V1::AuthorsController < Api::V1::BaseController
before_action :set_author, only: :show

def index
render json: { authors: Decidim::Dataspace::Author.all.map { |author| render_author(author) } }, status: :ok
end
preferred_locale = params[:preferred_locale].presence || "en"
authors = Author.from_proposals(preferred_locale)
return resource_not_found("Authors") if authors.blank?

def show
render json: render_author(@author), status: :ok
render json: authors, status: :ok
end

private

def render_author(author)
{
reference: author.reference,
name: author.name,
source: author.source
}
def show
render json: @author, status: :ok
end

def set_author
interoperable = Decidim::Dataspace::Interoperable.find_by(reference: params[:reference])
return resource_not_found("Author") unless interoperable

@author = Decidim::Dataspace::Author.find_by(interoperable_id: interoperable.id)
ref = CGI.unescape(params[:reference])
preferred_locale = params[:preferred_locale].presence || "en"
@author = Author.proposal_author(ref, preferred_locale)
return resource_not_found("Author") unless @author

@author
Expand Down
31 changes: 9 additions & 22 deletions app/controllers/decidim/dataspace/api/v1/containers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,21 @@ class Api::V1::ContainersController < Api::V1::BaseController
before_action :set_container, only: :show

def index
render json: { containers: Decidim::Dataspace::Container.all.map { |container| render_container(container) } }, status: :ok
end
preferred_locale = params["preferred_locale"] || "en"
containers = Container.from_proposals(preferred_locale)
return resource_not_found("Containers") if containers.blank?

def show
render json: render_container(@container), status: :ok
render json: containers, status: :ok
end

private

def render_container(container)
{
"reference": container.reference,
"source": container.source,
"name": container.name,
"description": container.description,
"metadata": container.metadata,
"created_at": container.created_at,
"updated_at": container.updated_at,
"deleted_at": container.deleted_at
}
def show
render json: @container, status: :ok
end

def set_container
interoperable = Decidim::Dataspace::Interoperable.find_by(reference: params[:reference])
return resource_not_found("Container") unless interoperable

@container = Decidim::Dataspace::Container.find_by(interoperable_id: interoperable.id)

ref = CGI.unescape(params[:reference])
preferred_locale = params["preferred_locale"] || "en"
@container = Container.from_params(ref, preferred_locale)
return resource_not_found("Container") unless @container

@container
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,32 @@
# frozen_string_literal: true

require "uri"

module Decidim
module Dataspace
class Api::V1::ContributionsController < Api::V1::BaseController
before_action :set_contribution, only: :show

def index
render json: { contributions: Decidim::Dataspace::Contribution.all.map { |contribution| render_contribution(contribution) } }
preferred_locale = params[:preferred_locale].presence || "en"
with_comments = params[:with_comments].presence
contributions = Contribution.from_proposals(preferred_locale, with_comments)
return resource_not_found("Contributions") if contributions.blank?

render json: contributions, status: :ok
end

def show
render json: render_contribution(@contribution)
render json: @contribution, status: :ok
end

private

def render_contribution(contribution)
{
reference: contribution.reference,
source: contribution.source,
container: contribution.container.reference,
locale: contribution.locale,
title: contribution.title,
content: contribution.content,
authors: contribution.authors&.map(&:reference),
metadata: contribution.metadata,
parent: contribution.parent&.reference,
created_at: contribution.created_at,
updated_at: contribution.updated_at,
deleted_at: contribution.deleted_at
}
end

def set_contribution
interoperable = Decidim::Dataspace::Interoperable.find_by(reference: params[:reference])
return resource_not_found("Contribution") unless interoperable

@contribution = Decidim::Dataspace::Contribution.find_by(interoperable_id: interoperable.id)
ref = CGI.unescape(params[:reference])
preferred_locale = params[:preferred_locale].presence || "en"
with_comments = params[:with_comments].presence
@contribution = Contribution.proposal(ref, preferred_locale, with_comments)
return resource_not_found("Contribution") unless @contribution

@contribution
Expand Down
47 changes: 4 additions & 43 deletions app/controllers/decidim/dataspace/api/v1/data_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,14 @@ module Decidim
module Dataspace
class Api::V1::DataController < Api::V1::BaseController
def index
locale = params[:preferred_locale].presence || "en"
render json:
{
containers: Decidim::Dataspace::Container.all.map { |container| render_container(container) },
contributions: Decidim::Dataspace::Contribution.all.map { |contribution| render_contribution(contribution) },
authors: Decidim::Dataspace::Author.all.map { |author| render_author(author) }
containers: Container.from_proposals(locale),
contributions: Contribution.from_proposals(locale),
authors: Author.from_proposals(locale)
}, status: :ok
end

private

def render_container(container)
{
"reference": container.reference,
"source": container.source,
"name": container.name,
"description": container.description,
"metadata": container.metadata,
"created_at": container.created_at,
"updated_at": container.updated_at,
"deleted_at": container.deleted_at
}
end

def render_contribution(contribution)
{
"reference": contribution.reference,
"source": contribution.source,
"container": contribution.container.reference,
"locale": contribution.locale,
"title": contribution.title,
"content": contribution.content,
"authors": contribution.authors&.map(&:reference),
"metadata": contribution.metadata,
"parent": contribution.parent&.reference,
"created_at": contribution.created_at,
"updated_at": contribution.updated_at,
"deleted_at": contribution.deleted_at
}
end

def render_author(author)
{
reference: author.reference,
name: author.name,
source: author.source
}
end
end
end
end
47 changes: 47 additions & 0 deletions app/helpers/decidim/proposals/external_proposal_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

module Decidim
module Proposals
module ExternalProposalHelper
def external_state_item(state)
return if state["state"].blank?

if state["withdrawn"] == true
content_tag(:span, humanize_proposal_state(:withdrawn), class: "label alert")
elsif state["emendation"] == true
content_tag(:span, humanize_proposal_state(state["state"].capitalize), class: "label #{external_state_class(state)}")
else
content_tag(:span, translated_attribute(state["state"].capitalize), class: "label", style: external_css_style(state))
end
end

def external_state_class(state)
return "alert" if state["withdrawn"] == "true"

case state["state"]
when "accepted"
"success"
when "rejected"
"alert"
when "evaluating"
"warning"
else
"muted"
end
end

def external_css_style(state)
case state["state"]
when "accepted"
"background-color: #E3FCE9; color: #15602C; border-color: #15602C;"
when "rejected"
"background-color: #FFEBE9; color: #D1242F; border-color: #D1242F;"
when "evaluating"
"background-color: #FFF1E5; color: #BC4C00; border-color: #BC4C00;"
else
""
end
end
end
end
end
59 changes: 59 additions & 0 deletions app/models/decidim/dataspace/author.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,65 @@ class Author < Decidim::Dataspace::Interoperable
# rubocop:enable Rails/HasAndBelongsToMany

delegate :reference, :source, :created_at, :updated_at, :deleted_at, to: :interoperable

def self.from_proposals(preferred_locale)
proposals = Decidim::Proposals::Proposal.published
.not_hidden
.only_amendables
locale = "en"
available_locales = proposals.first&.organization&.available_locales
locale = preferred_locale if available_locales.present? && available_locales.include?(preferred_locale)

authors = proposals.all.map do |proposal|
proposal.authors.map do |author|
Author.display_author(author, locale)
end
end
authors.compact.flatten.uniq { |hash| hash[:reference] }
end

def self.proposal_author(reference, preferred_locale)
if Decidim::User.find_by(name: reference) || Decidim::UserGroup.find_by(name: reference)
author = Decidim::User.find_by(name: reference) || Decidim::UserGroup.find_by(name: reference)
Author.user_or_group_author(author)
elsif Decidim::Organization.find_by(reference_prefix: reference)
author = Decidim::Organization.find_by(reference_prefix: reference)
locale = author.available_locales.include?(preferred_locale) ? preferred_locale : "en"
Author.organization_author(author, locale)
elsif Decidim::Meetings::Meeting.find_by(reference:)
author = Decidim::Meetings::Meeting.find_by(reference:)
locale = author.organization.available_locales.include?(preferred_locale) ? preferred_locale : "en"
Author.meeting_author(author, locale)
end
end

def self.display_author(author, locale)
if author.instance_of?(Decidim::Organization)
Author.organization_author(author, locale)
elsif author.instance_of?(Decidim::Meetings::Meeting)
Author.meeting_author(author, locale)
elsif author.instance_of?(Decidim::User) || author.instance_of?(Decidim::UserGroup)
Author.user_or_group_author(author)
end
end

def self.organization_author(author, locale)
{ reference: author.reference_prefix,
name: author.name[locale],
source: author.official_url }
end

def self.meeting_author(author, locale)
{ reference: author.reference,
name: author.title[locale],
source: Decidim::ResourceLocatorPresenter.new(author).url }
end

def self.user_or_group_author(author)
{ reference: author.name,
name: author.name,
source: author.personal_url }
end
end
end
end
Loading
Loading