diff --git a/Gemfile b/Gemfile index 74c906c..bb72f4b 100644 --- a/Gemfile +++ b/Gemfile @@ -24,8 +24,8 @@ gem "bootsnap", require: false # gem "image_processing", "~> 1.2" # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin Ajax possible -# gem "rack-cors" -# +gem "rack-cors" + group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index 30ad043..6dc8c51 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -293,6 +293,9 @@ GEM raabro (1.4.0) racc (1.8.1) rack (3.1.16) + rack-cors (3.0.0) + logger + rack (>= 3.0.14) rack-session (2.1.1) base64 (>= 0.1.0) rack (>= 3.0.0) @@ -435,6 +438,7 @@ DEPENDENCIES pg (~> 1.1) propshaft (~> 1.1) puma (>= 5.0) + rack-cors rails (~> 8.0.2) rubocop-rails-omakase ruby_llm (~> 1.3)! diff --git a/app/controllers/admin/evidence_controller.rb b/app/controllers/admin/evidence_controller.rb new file mode 100644 index 0000000..503e1c9 --- /dev/null +++ b/app/controllers/admin/evidence_controller.rb @@ -0,0 +1,61 @@ +module Admin + class EvidencesController < ApplicationController + def index + evidences = Evidence.all + + if params[:parliament_session_id].present? + evidences = evidences.where(parliament_session_id: params[:parliament_session_id]) + end + + if params[:search].present? + query = "%#{params[:search]}%" + evidences = evidences.where( + "title ILIKE :q OR description ILIKE :q OR source_url ILIKE :q", + q: query + ) + end + + evidences = evidences + .order(created_at: :desc) + .limit(params[:limit] || 25) + .offset(params[:offset] || 0) + + render json: { + data: evidences.as_json(only: [ :id, :title, :description, :source_url, :parliament_session_id ]), + meta: { + total: evidences.count + } + } + end + + def show + evidence = Evidence.find(params[:id]) + render json: evidence.as_json(include: { promises: { only: [ :id, :title ] } }) + end + + def update + evidence = Evidence.find(params[:id]) + + if evidence.update(evidence_params) + render json: { success: true, data: evidence } + else + render json: { success: false, errors: evidence.errors.full_messages }, status: :unprocessable_entity + end + end + + def destroy + evidence = Evidence.find(params[:id]) + if evidence.destroy + render json: { success: true, message: "Evidence deleted." } + else + render json: { success: false, errors: evidence.errors.full_messages }, status: :unprocessable_entity + end + end + + private + + def evidence_params + params.require(:evidence).permit(:title, :description, :source_url, :parliament_session_id, promise_ids: []) + end + end +end diff --git a/app/controllers/admin/promises_controller.rb b/app/controllers/admin/promises_controller.rb new file mode 100644 index 0000000..1e608d1 --- /dev/null +++ b/app/controllers/admin/promises_controller.rb @@ -0,0 +1,71 @@ +module Admin + class PromisesController < ApplicationController + skip_before_action :verify_authenticity_token + def index + base_query = Promise.all + + base_query = base_query.where(party_code: params[:party_code]) if params[:party_code].present? + base_query = base_query.where(region_code: params[:region_code]) if params[:region_code].present? + base_query = base_query.where(parliament_session_id: params[:parliament_session_id]) if params[:parliament_session_id].present? + base_query = base_query.where(source_type: params[:source_type]) if params[:source_type].present? && params[:source_type] != "all" + + if params[:bc_promise_rank].present? + if params[:bc_promise_rank] == "none" + base_query = base_query.where(bc_promise_rank: nil) + elsif params[:bc_promise_rank] != "all" + base_query = base_query.where(bc_promise_rank: params[:bc_promise_rank]) + end + end + + if params[:search].present? + query = "%#{params[:search]}%" + base_query = base_query.where("title ILIKE :q OR description ILIKE :q", q: query) + end + + total = base_query.count + promises = base_query.order(created_at: :desc) + .limit(params[:limit] || 25) + .offset(params[:offset] || 0) + + render json: { + data: promises.as_json(only: Promise.client_fields), + meta: { total: total } + } + end + + def update + promise = Promise.find(params[:id]) + puts "\nšŸ”§ Updating Promise ID: #{promise.id}" + puts "āž”ļø Incoming Params: #{params.to_unsafe_h.slice(*Promise.client_fields.map(&:to_s))}" + + if promise.update(promise_params) + puts "āœ… Update Successful" + render json: { success: true, data: promise.as_json(only: Promise.client_fields) } + else + puts "āŒ Update Failed: #{promise.errors.full_messages.join(', ')}" + render json: { success: false, errors: promise.errors.full_messages }, status: :unprocessable_entity + end + end + + def destroy + promise = Promise.find(params[:id]) + puts "šŸ—‘ļø Soft deleting promise #{promise.id}" + + if promise.update( + status: "deleted", + ) + puts "āœ… Soft delete successful" + render json: { success: true, message: "Promise soft deleted", id: promise.id } + else + puts "āŒ Soft delete failed: #{promise.errors.full_messages.join(', ')}" + render json: { success: false, errors: promise.errors.full_messages }, status: :unprocessable_entity + end + end + + private + + def promise_params + params.permit(*Promise.client_fields - [ :id ]) + end + end +end diff --git a/app/controllers/departments_controller.rb b/app/controllers/departments_controller.rb index e6f2ea9..0252f42 100644 --- a/app/controllers/departments_controller.rb +++ b/app/controllers/departments_controller.rb @@ -3,9 +3,8 @@ class DepartmentsController < ApplicationController # GET /departments` def index - @departments = Department.all - - render json: @departments + @departments = Department.order(:display_name) + render json: @departments.as_json(only: [ :id, :display_name, :slug, :priority, :official_name ]) end # GET /departments/1 diff --git a/app/helpers/admin/evidence_helper.rb b/app/helpers/admin/evidence_helper.rb new file mode 100644 index 0000000..644130e --- /dev/null +++ b/app/helpers/admin/evidence_helper.rb @@ -0,0 +1,2 @@ +module Admin::EvidenceHelper +end diff --git a/app/helpers/admin/promises_helper.rb b/app/helpers/admin/promises_helper.rb new file mode 100644 index 0000000..783837b --- /dev/null +++ b/app/helpers/admin/promises_helper.rb @@ -0,0 +1,2 @@ +module Admin::PromisesHelper +end diff --git a/app/models/activity_extractor.rb b/app/models/activity_extractor.rb index 8db437c..8fdf31a 100644 --- a/app/models/activity_extractor.rb +++ b/app/models/activity_extractor.rb @@ -29,7 +29,7 @@ def prompt(promises, entry) If no relevant activities are found in the political artifact, state this clearly in your response. Do not mention the promise_id in your impact_reasoning. - + Try to minimize the number of activities listed, they should be combined if they are similar enough to avoid redundancy. Remember to focus only on activities that could potentially impact the government's progress on their promises. Do not include unrelated information or speculate beyond what is reasonably implied by the artifact. diff --git a/app/models/promise.rb b/app/models/promise.rb index da77f54..56a3d9c 100644 --- a/app/models/promise.rb +++ b/app/models/promise.rb @@ -10,4 +10,50 @@ def format_for_llm description: description } end + + def self.client_fields + [ + :id, + :text, + :concise_title, + :description, + + :bc_promise_rank, + :bc_promise_direction, + :bc_promise_rank_rationale, + :source_type, + + :responsible_department_lead, + :reporting_lead_title, + :category, + :parliament_session_id, + :date_issued, + :status, + + :progress_score, + :progress_summary, + + :what_it_means_for_canadians, + :intended_impact_and_objectives, + :background_and_context, + + :linked_evidence_ids, + :commitment_history_rationale, + + :region_code, + :party_code, + :deleted_at, + :deleted_by_admin, + :department, + :evidence_ids, + + # Read-only fields + :region_code, + :party_code, + :migration_metadata, + :ingested_at, + :explanation_enriched_at, + :linking_preprocessing_done_at + ] + end end diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index 0c5dd99..a5e8ac8 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -5,12 +5,12 @@ # Read more: https://github.com/cyu/rack-cors -# Rails.application.config.middleware.insert_before 0, Rack::Cors do -# allow do -# origins "example.com" -# -# resource "*", -# headers: :any, -# methods: [:get, :post, :put, :patch, :delete, :options, :head] -# end -# end +Rails.application.config.middleware.insert_before 0, Rack::Cors do + allow do + origins "http://localhost:3001", "https://www.buildcanada.com" + + resource "*", + headers: :any, + methods: [ :get, :post, :put, :patch, :delete, :options, :head ] + end +end diff --git a/config/routes.rb b/config/routes.rb index 44842ed..62b3a15 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,6 +18,11 @@ resources :promises, only: [ :index, :show ] resources :evidences, only: [ :index, :show ] + namespace :admin do + resources :promises, only: [ :index, :show, :update, :destroy ] + resources :evidence, only: [ :index, :show, :update, :destroy ] + end + # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. diff --git a/db/schema.rb b/db/schema.rb index 94df139..5620703 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -131,6 +131,7 @@ t.string "url" t.datetime "last_scraped", precision: nil t.jsonb "raw" + t.string "source_url" t.bigint "government_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false diff --git a/test/controllers/admin/evidence_controller_test.rb b/test/controllers/admin/evidence_controller_test.rb new file mode 100644 index 0000000..5239555 --- /dev/null +++ b/test/controllers/admin/evidence_controller_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class Admin::EvidenceControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/controllers/admin/promises_controller_test.rb b/test/controllers/admin/promises_controller_test.rb new file mode 100644 index 0000000..0de59d5 --- /dev/null +++ b/test/controllers/admin/promises_controller_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class Admin::PromisesControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end