Skip to content

Commit

Permalink
MAM-2669-breadcrumbs-and-transparency-foryou-posts (#193)
Browse files Browse the repository at this point in the history
* save origin reason when adding status to a user's for you feed
* fetch status origin api GET `https://staging.moth.social/api/v3/timelines/for_you/statuses/11129536085876024`
* Trigger rebuild of FY when unsubscribing from smartlist
  • Loading branch information
jtomchak authored Oct 25, 2023
1 parent 3f16332 commit 541a041
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 7 deletions.
3 changes: 3 additions & 0 deletions app/controllers/api/v3/channels_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ def subscribe
render json: @user
end

# Trigger user rebuild when unsubscribing from channel
# this is to clear out unwanted content from FY Feed
def unsubscribe
@mammoth = Mammoth::Channels.new
@user = @mammoth.unsubscribe(channel_id_param, acct_param)
UpdateForYouWorker.perform_async({ acct: acct_param, rebuild: true })
render json: @user
end

Expand Down
21 changes: 21 additions & 0 deletions app/controllers/api/v3/timelines/statuses_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

class Api::V3::Timelines::StatusesController < Api::BaseController
before_action :require_mammoth!

rescue_from Mammoth::StatusOrigin::NotFound do |e|
render json: { error: e.to_s }, status: 404
end

def show
origin = Mammoth::StatusOrigin.instance
@origins = origin.find(status_id_param)
render json: @origins, each_serializer: StatusOriginSerializer
end

private

def status_id_param
params.require(:id)
end
end
13 changes: 10 additions & 3 deletions app/lib/mammoth/channels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,32 @@ class NotFound < StandardError; end
def channels_with_statuses
list(include_accounts: true).each do |channel|
account_ids = account_ids(channel[:accounts])
channel[:statuses] = statuses_from_channel_accounts(account_ids)
channel[:statuses] = statuses_from_channels(account_ids)
end
end

# Used in ForYou Feed
# Get Statuses from array of channels
# filter out based on per channel threshold
def select_channels_with_statuses(channels)
origin = Mammoth::StatusOrigin.instance
channels.flat_map do |channel|
account_ids = account_ids(channel[:accounts])
statuses_from_channel_accounts(account_ids).filter_map { |s| engagment_threshold(s, channel[:fy_engagement_threshold]) }
statuses_with_accounts_from_channels(account_ids).filter_map { |s| engagment_threshold(s, channel[:fy_engagement_threshold]) }
.each { |s| origin.add_channel(s, channel) }
end
end

def statuses_from_channel_accounts(account_ids)
def statuses_from_channels(account_ids)
Status.where(account_id: account_ids,
created_at: (GO_BACK.hours.ago)..Time.current)
end

def statuses_with_accounts_from_channels(account_ids)
Status.includes([:account]).where(account_id: account_ids,
created_at: (GO_BACK.hours.ago)..Time.current)
end

# Check status for Channel's set level of engagment
# Filter out polls and replys
def engagment_threshold(wrapped_status, channel_engagment_setting)
Expand Down
67 changes: 67 additions & 0 deletions app/lib/mammoth/status_origin.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true
# rubocop:disable all
require 'singleton'

module Mammoth

class StatusOrigin
include Singleton
include Redisable
class NotFound < StandardError; end


# Add Status and Reason to list
def add_channel(status, channel)
list_key = key(status[:id])
reason = channel_reason(status, channel)

add_reason(list_key, reason)
end

def add_mammoth_pick(status)
list_key = key(status[:id])
reason = mammoth_pick_reason(status)

add_reason(list_key, reason)
end

# Add reason by key id
# Expire Reason in 7 days
def add_reason(key, reason)
redis.sadd(key, reason)
redis.expire(key, 7.day.seconds)
end

def find(status_id)
list_key = key(status_id)
results = redis.smembers(list_key).map { |o|
payload = Oj.load(o, symbol_keys: true)
originating_account = Account.create(payload[:originating_account])
# StatusOrigin Active Model for serialization
::StatusOrigin.new(source: payload[:source], channel_id: payload[:channel_id], title: payload[:title], originating_account:originating_account )
}
# Throw Error if array find is empty
raise NotFound, 'status not found' unless results.length > 0
return results
end

private

# Redis key of a status
# @param [Integer] status id
# @param [Symbol] subtype
# @return [String]
def key(id, subtype = nil)
return "origin:for_you:#{id}" unless subtype
"origin:for_you:#{id}:#{subtype}"
end

def channel_reason(status, channel)
Oj.dump({source: "SmartList", channel_id: channel[:id], title: channel[:title], originating_account: status.account})
end

def mammoth_pick_reason(status)
Oj.dump({source: "MammothPick", originating_account: status.account })
end
end
end
11 changes: 11 additions & 0 deletions app/models/status_origin.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# ActiveModel Only for Serialization
class StatusOrigin
include ActiveModel::Model
include ActiveModel::Serialization

attr_accessor :source, :channel_id, :title, :originating_account

def initialize(attributes = {})
super
end
end
7 changes: 7 additions & 0 deletions app/serializers/status_origin_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Required Source & Originating Account
# Channel_id & Title maybe be null
class StatusOriginSerializer < ActiveModel::Serializer
attributes :source, :title, :channel_id

belongs_to :originating_account, serializer: REST::AccountSerializer
end
10 changes: 6 additions & 4 deletions app/workers/update_for_you_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,7 @@ def push_channels_status
user_setting = @user[:for_you_settings]
return if user_setting[:from_your_channels].zero?

@personal.statuses_for_enabled_channels(@user)
.filter_map { |s| engagment_threshold(s, user_setting[:from_your_channels], 'channel') }
.each { |s| ForYouFeedWorker.perform_async(s['id'], @account.id, 'personal') }
@personal.statuses_for_enabled_channels(@user).each { |s| ForYouFeedWorker.perform_async(s['id'], @account.id, 'personal') }
end

# Mammoth Curated OG List
Expand All @@ -106,9 +104,13 @@ def push_mammoth_curated_status

curated_list = Mammoth::CuratedList.new
list_statuses = curated_list.curated_list_statuses
origin = Mammoth::StatusOrigin.instance

list_statuses.filter_map { |s| engagment_threshold(s, user_setting[:curated_by_mammoth], 'mammoth') }
.each { |s| ForYouFeedWorker.perform_async(s['id'], @account.id, 'personal') }
.each do |s|
origin.add_mammoth_pick(s)
ForYouFeedWorker.perform_async(s['id'], @account.id, 'personal')
end
end

# Check status for User's level of engagment
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,7 @@
namespace :timelines do
resources :channels, only: :show, controller: :channels
resource :for_you, only: [:show], controller: 'for_you' do
resources :statuses, only: :show, controller: :statuses
get '/me', to: 'for_you#index'
put '/me', to: 'for_you#update'
end
Expand Down
4 changes: 4 additions & 0 deletions spec/fabricators/status_origin_fabricator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fabricator(:status_origin) do
source "MyString"
title "MyString"
end
5 changes: 5 additions & 0 deletions spec/models/status_origin_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require 'rails_helper'

RSpec.describe StatusOrigin, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end

0 comments on commit 541a041

Please sign in to comment.