diff --git a/decidim-ai/lib/decidim/ai/spam_detection/resource/base.rb b/decidim-ai/lib/decidim/ai/spam_detection/resource/base.rb index f2fee86815d85..3482f158bb86b 100644 --- a/decidim-ai/lib/decidim/ai/spam_detection/resource/base.rb +++ b/decidim-ai/lib/decidim/ai/spam_detection/resource/base.rb @@ -40,7 +40,7 @@ def error_message(klass, method_name) end def resource_hidden?(resource) - resource.class.included_modules.include?(Decidim::Reportable) && resource.hidden? && report_reasons.exclude?(resource.reports&.last&.reason) + resource.class.included_modules.include?(Decidim::Reportable) && resource.hidden? && report_reasons.include?(resource.reports&.last&.reason) end def report_reasons diff --git a/decidim-ai/spec/lib/decidim/ai/spam_detection/importer/database_spec.rb b/decidim-ai/spec/lib/decidim/ai/spam_detection/importer/database_spec.rb index 667abd5db0afa..0cb1ddaf4fd1a 100644 --- a/decidim-ai/spec/lib/decidim/ai/spam_detection/importer/database_spec.rb +++ b/decidim-ai/spec/lib/decidim/ai/spam_detection/importer/database_spec.rb @@ -11,6 +11,30 @@ Decidim::Ai::SpamDetection.resource_models = resources end + shared_examples "some resources are being spam" do + before do + Decidim::Ai::SpamDetection.resource_models = resource_models + allow(Decidim::Ai::SpamDetection).to receive(:resource_classifier).and_return(instance) + end + + let(:reporting_user) { author } + let(:spam_count) { 2 } + let!(:parent) { create(:report, reason: "parent_hidden", user: reporting_user, moderation: create(:moderation, :hidden, reportable: resources.last)) } + Decidim::Report::REASONS.excluding("parent_hidden").each do |reason| + let!(:report) { create(:report, reason:, user: reporting_user, moderation: create(:moderation, :hidden, reportable:)) } + + it "successfully loads the dataset when there are resources marked as #{reason}" do + allow(instance).to receive(:train) + + described_class.call + + expect(instance).to have_received(:train).with(:ham, anything).at_least(training - spam_count) + expect(instance).to have_received(:train).with(:spam, anything).at_least(spam_count) + expect(instance).to have_received(:train).with(:spam, "Hidden resource").at_least(1) + end + end + end + shared_examples "resource is being indexed" do let(:organization) { create(:organization) } let!(:author) { create(:user, organization:) } @@ -56,67 +80,64 @@ let(:manifest_name) { "meetings" } let(:training) { 20 } - let!(:meetings) do - create_list(:meeting, 4, component:, author:, - title: { en: "Some proposal that is not blocked" }, - description: { en: "The body for the meeting." }) - end + let!(:reportable) { create(:meeting, component:, author:, title: { en: "Hidden resource" }) } + let!(:resources) { create_list(:meeting, 3, component:, author:) } + let(:resource_models) { { "Decidim::Meetings::Meeting" => "Decidim::Ai::SpamDetection::Resource::Meeting" } } include_examples "resource is being indexed" + include_examples "some resources are being spam" do + let(:spam_count) { 5 } + end end context "when trained model is Decidim::Proposals::Proposal" do let(:manifest_name) { "proposals" } let(:training) { 8 } - let!(:proposals) do - create_list(:proposal, 4, - :published, - component:, - users: [author], - title: "Some proposal that is not blocked", - body: "The body for the proposal.") - end + let!(:reportable) { create(:proposal, :published, component:, users: [author], title: { en: "Hidden resource" }) } + let!(:resources) { create_list(:proposal, 3, :published, component:, users: [author]) } let(:resource_models) { { "Decidim::Proposals::Proposal" => "Decidim::Ai::SpamDetection::Resource::Proposal" } } include_examples "resource is being indexed" + include_examples "some resources are being spam" end context "when trained model is Decidim::Proposals::CollaborativeDraft" do let(:manifest_name) { "proposals" } let(:training) { 8 } - let!(:collaborative_drafts) do - create_list(:collaborative_draft, 4, - component:, - users: [author], - title: "Some draft that is not blocked", - body: "The body for the proposal.") - end + let!(:reportable) { create(:collaborative_draft, component:, users: [author], title: "Hidden resource") } + let!(:resources) { create_list(:collaborative_draft, 3, component:, users: [author]) } let(:resource_models) { { "Decidim::Proposals::CollaborativeDraft" => "Decidim::Ai::SpamDetection::Resource::CollaborativeDraft" } } include_examples "resource is being indexed" + include_examples "some resources are being spam" end context "when trained model is Decidim::Debates::Debate" do let(:manifest_name) { "debates" } let(:training) { 8 } - let!(:debates) do - create_list(:debate, 4, + let!(:reportable) do + create(:debate, + author:, component:, + title: { en: "Hidden resource" }) + end + let!(:resources) do + create_list(:debate, 3, author:, component:, - title: { en: "Some proposal that is not blocked" }, - description: { en: "The body for the meeting." }) + title: { en: "Some proposal that is not blocked" }) end let(:resource_models) { { "Decidim::Debates::Debate" => "Decidim::Ai::SpamDetection::Resource::Debate" } } include_examples "resource is being indexed" + include_examples "some resources are being spam" end context "when trained model is Decidim::User" do let(:tested) { 3 } - let(:training) { tested + 1 } # tested + author in shared example + let(:training) { 4 } # tested + author in shared example let!(:user) { create_list(:user, tested, organization:, about: "Something about me") } let(:resource_models) { { "Decidim::User" => "Decidim::Ai::SpamDetection::Resource::UserBaseEntity" } } diff --git a/decidim-core/app/helpers/decidim/menu_helper.rb b/decidim-core/app/helpers/decidim/menu_helper.rb index d2735bd43afb6..77213b63b64ef 100644 --- a/decidim-core/app/helpers/decidim/menu_helper.rb +++ b/decidim-core/app/helpers/decidim/menu_helper.rb @@ -57,7 +57,7 @@ def footer_menu self, element_class: "font-semibold underline", active_class: "is-active", - container_options: { class: "space-y-4 break-inside-avoid", role: :menu }, + container_options: { class: "space-y-4 break-inside-avoid" }, label: t("layouts.decidim.footer.decidim_title") ) end diff --git a/decidim-core/app/presenters/decidim/menu_item_presenter.rb b/decidim-core/app/presenters/decidim/menu_item_presenter.rb index 9354e88169528..da2454f00700a 100644 --- a/decidim-core/app/presenters/decidim/menu_item_presenter.rb +++ b/decidim-core/app/presenters/decidim/menu_item_presenter.rb @@ -29,7 +29,7 @@ def initialize(menu_item, view, options = {}) delegate :content_tag, :safe_join, :link_to, :active_link_to_class, :is_active_link?, :icon, to: :@view def render - content_tag :li, role: :menuitem, class: link_wrapper_classes do + content_tag :li, class: link_wrapper_classes do output = if url == "#" [content_tag(:span, composed_label, class: "sidebar-menu__item-disabled")] else diff --git a/decidim-core/spec/system/download_your_data_spec.rb b/decidim-core/spec/system/download_your_data_spec.rb index f4061c8cab788..87bedc460cdcf 100644 --- a/decidim-core/spec/system/download_your_data_spec.rb +++ b/decidim-core/spec/system/download_your_data_spec.rb @@ -103,6 +103,7 @@ describe "Export data" do it "exports an archive with all user information" do expect(Decidim::PrivateExport.count).to eq(4) + sleep 2 perform_enqueued_jobs { click_on "Request" } within_flash_messages do diff --git a/decidim-participatory_processes/config/locales/en.yml b/decidim-participatory_processes/config/locales/en.yml index 4fe4ca6450126..19a1b7c60a4b8 100644 --- a/decidim-participatory_processes/config/locales/en.yml +++ b/decidim-participatory_processes/config/locales/en.yml @@ -429,7 +429,7 @@ en: three: 40 - 60% two: 20 - 40% citizen_influence_level: - legend: This indicator measures the degree of actual citizen power in a process, specifically quantifying the level of decisional and executive authority citizens hold, from simply receiving information to decisively influencing decisions and managing resources. + legend: To what extent do citizens hold decisional authority in this process, ranging from simply receiving information to decisively influencing decisions and managing resources? options: five: Participants were involved in the decision-making and the management of resources or the implementation four: Participants were involved decisively in the decision-making @@ -719,55 +719,23 @@ en:
The questions that make up the self-assessment form answered by the administrators, as well as the values associated with each answer, are set out below.
-Inclusivity according to cultural origin and functional diversity.
+Impact relevance, as the percentage of the organisation's budget, of the organisation's workforce, or of the total population, that will be affected by the process' result, and the time of such impact.
- -One-way flow of information—from officials to citizens—with no channel provided for feedback and no power for negotiation.
-A participatory process where citizens are asked for their opinions, typically through surveys or meetings, but without assurance that their input will influence decisions.
-A participatory process where citizens are given limited influence, often in a tokenistic manner, such as inclusion in boards or committees, but without real power, as decision-making remains controlled by the traditional power elite.
-A participatory process where citizens and organizations share decision-making power, allowing participants to negotiate, allocate resources, and influence planning, with rules that cannot be changed unilaterally. It often emerges from citizen mobilization and collective action.
-A participatory process where citizens gain authority, control, or funding to influence and manage an organization.
-A highest level of participation where citizens fully govern a program or institution, managing policies, resources, and decision-making without external interference.
-