Class: SourceMonitor::SourcesController

Inherits:
ApplicationController show all
Includes:
ActionView::RecordIdentifier, SanitizesSearchParams
Defined in:
app/controllers/source_monitor/sources_controller.rb

Constant Summary collapse

ITEMS_PREVIEW_LIMIT =
SourceMonitor::Scraping::BulkSourceScraper::DEFAULT_PREVIEW_LIMIT
PER_PAGE =
25
SEARCH_FIELD =
:name_or_feed_url_or_website_url_cont

Instance Method Summary collapse

Instance Method Details

#createObject



82
83
84
85
86
87
88
89
90
91
# File 'app/controllers/source_monitor/sources_controller.rb', line 82

def create
  @source = Source.new(source_params)

  if @source.save
    enqueue_favicon_fetch(@source)
    redirect_to source_monitor.source_path(@source), notice: "Source created successfully"
  else
    render :new, status: :unprocessable_entity
  end
end

#destroyObject



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'app/controllers/source_monitor/sources_controller.rb', line 113

def destroy
  search_params = sanitized_search_params

  begin
    unless @source.destroy
      handle_destroy_failure(search_params, "Could not delete source: #{@source.errors.full_messages.join(', ')}")
      return
    end
  rescue ActiveRecord::InvalidForeignKey
    handle_destroy_failure(search_params, "Cannot delete source: other records still reference it. Remove dependent records first.")
    return
  end

  message = "Source deleted"

  respond_to do |format|
    format.turbo_stream do
      query = build_search_query(params: search_params)

      metrics = SourceMonitor::Analytics::SourcesIndexMetrics.new(
        base_scope: Source.all,
        result_scope: query.result,
        search_params:
      )

      redirect_location = SourceMonitor::Security::ParameterSanitizer.safe_redirect_path(params[:redirect_to])

      responder = SourceMonitor::TurboStreams::StreamResponder.new
      presenter = SourceMonitor::Sources::TurboStreamPresenter.new(source: @source, responder:)

      presenter.render_deletion(
        metrics:,
        query:,
        search_params:,
        redirect_location:
      )

      responder.toast(message:, level: :success)

      render turbo_stream: responder.render(view_context)
    end

    format.html do
      redirect_to source_monitor.sources_path, notice: message
    end
  end
end

#editObject



93
94
# File 'app/controllers/source_monitor/sources_controller.rb', line 93

def edit
end

#indexObject



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'app/controllers/source_monitor/sources_controller.rb', line 21

def index
  @search_params = sanitized_search_params
  expand_scrape_recommendation_filter
  @q = build_search_query(params: @search_params)

  @paginator = SourceMonitor::Pagination::Paginator.new(
    scope: @q.result,
    page: params[:page],
    per_page: params[:per_page] || PER_PAGE
  ).paginate

  @sources = @paginator.records

  @search_term = @search_params[SEARCH_FIELD.to_s].to_s.strip
  @search_field = SEARCH_FIELD

  metrics = SourceMonitor::Analytics::SourcesIndexMetrics.new(
    base_scope: Source.all,
    result_scope: @paginator.records,
    search_params: @search_params
  )

  @recent_import_histories = SourceMonitor::ImportHistory.not_dismissed.recent_for(source_monitor_current_user&.id).limit(5)

  @fetch_interval_distribution = metrics.fetch_interval_distribution
  @fetch_interval_filter = metrics.fetch_interval_filter
  @selected_fetch_interval_bucket = metrics.selected_fetch_interval_bucket
  @item_activity_rates = metrics.item_activity_rates

  source_ids = @sources.map(&:id)
  word_counts = metrics.word_count_averages(source_ids)
  @avg_feed_word_counts = word_counts[:feed]
  @avg_scraped_word_counts = word_counts[:scraped]

  @scrape_recommendations = SourceMonitor::Analytics::ScrapeRecommendations.new
  @scrape_candidate_ids = Set.new(@scrape_recommendations.candidate_ids_for(source_ids))
  @total_scrape_candidate_count = @scrape_recommendations.candidates_count

  # Row partial preload requirements (V3): item_activity_rates,
  # avg_feed_word_counts, avg_scraped_word_counts are pre-computed above
  # and passed as locals to avoid N+1 queries in _row.html.erb.
  adapter_options = Source.distinct.where.not(scraper_adapter: [ nil, "" ]).order(:scraper_adapter).pluck(:scraper_adapter)
  @filter_presenter = SourceMonitor::SourcesFilterPresenter.new(
    search_params: @search_params,
    search_term: @search_term,
    fetch_interval_filter: @fetch_interval_filter,
    adapter_options: adapter_options
  )
end

#newObject



78
79
80
# File 'app/controllers/source_monitor/sources_controller.rb', line 78

def new
  @source = Source.new(default_attributes)
end

#showObject



71
72
73
74
75
76
# File 'app/controllers/source_monitor/sources_controller.rb', line 71

def show
  @recent_fetch_logs = @source.fetch_logs.order(started_at: :desc).limit(5)
  @recent_scrape_logs = @source.scrape_logs.order(started_at: :desc).limit(5)
  @items = @source.items.recent.includes(:item_content).limit(ITEMS_PREVIEW_LIMIT)
  @bulk_scrape_selection = :current
end

#updateObject



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'app/controllers/source_monitor/sources_controller.rb', line 96

def update
  scraping_was_disabled = !@source.scraping_enabled?

  if @source.update(source_params)
    notice = "Source updated successfully"

    if scraping_was_disabled && @source.scraping_enabled?
      enqueued = enqueue_unscraped_items(@source)
      notice = "Auto-scraping enabled. #{enqueued} existing #{'item'.pluralize(enqueued)} queued for scraping."
    end

    redirect_to source_monitor.source_path(@source), notice: notice
  else
    render :edit, status: :unprocessable_entity
  end
end