Commit graph

71 commits

Author SHA1 Message Date
Ben Halpern
4d7fcb7359
Quick fix to pagination in /stories/feed (#19423)
* Quick fix to pagination in /stories/feed

* Fix whitespace

* Update app/services/articles/feeds/variant_query.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Mac Siri <mac@forem.com>
2023-05-09 09:52:03 -03:00
Anna Buianova
784afdf41e
Routine rubocop fixes (#19254)
* Rubocop fixes

* Rubocop fixes

* Fixed rubocop violation

* Fixed policies rubocop violations

* More rubocop fixes
2023-03-24 14:37:44 +03:00
Joshua Wehner
b812ca5955
(Really) Enable multiple reactions on #index (#19202) 2023-03-22 10:20:00 -04:00
Joshua Wehner
727705dc89
Revert "Enable multiple reactions on #index (including front-end render)" (#19197) 2023-03-03 09:59:51 -05:00
Joshua Wehner
8a45a4bf67
Enable multiple reactions on #index (including front-end render) (#19169)
* Revert "(Temporarily) Remove multiple reactions from #index (#19142)"

This reverts commit a45d300639.

* Clean up reaction category count mechanism for #index

* Remove, unnecessary

* Expose reaction categories in JSON response

* Update test with new categories

* Fix flaky sample / minimum floor

* buildArticle (infinite scroll) with multiple reactions

* If we're doing #index front-end, we aren't feature-flag'd

* nbsp

* react (home feed) has multiple_reactions

* Update app/assets/javascripts/utilities/buildArticleHTML.js

Co-authored-by: Rajat Talesra <rajat@forem.com>

* Clean up Reactable#reaction_categories

* Use 'multiple_reactions_icons_container'

* Try adding a ReactionCount test

* Adapt memory fix from 76dd53d

* Try adding categories to fixture

* Attempt to eager-load distinct public categories

* Setting dependent to the default for rubocop

* Try making image assets more public?

* Revert "Fix flaky sample / minimum floor"

---------

Co-authored-by: Rajat Talesra <rajat@forem.com>
Co-authored-by: Mac Siri <mac@forem.com>
2023-03-03 15:17:19 +01:00
Joshua Wehner
1374ab98e6
Ensure spam doesn't hit logged-out /latest (#19110)
* Ensure spam doesn't hit logged-out /latest

* Restore load-bearing constant

* Narrow selector avoids the new 'latest' banner

* Test scenario for signed-in low-score content

* Remove false-negative test

* Actual test for the logged-in scenario

* Signed-in users get poor quality content

* Feeds (RSS) use an entirely different query

* Pass @stories

* Update tests with new minimum score
2023-02-17 09:29:59 +01:00
Ben Halpern
ef63738454
Feed experiment for 10-11-2022 (#18566) 2022-10-11 08:56:49 -06:00
Ben Halpern
f70bc2b0b8
Add internal tool to preview feed configs (#18544)
* Add internal tool to preview feed configs

* Update app/services/articles/feeds/variant_query.rb
2022-10-06 07:48:44 -04:00
Jeremy Friesen
19e5b6da35
Ensuring home feed uses minimum score threshold (#18123)
Prior to this commit we had a hard-coded 0; there are other hard-coded
score ranges throughout the code-base.  These are unexplained magic
numbers.  The hope with this change is to provide a mechanism for Forem
owners to adjust a configuration and see a change in behavior.

Related to forem/forem-internal-eng#484
2022-07-15 16:37:51 -04:00
Anna Buianova
80ce8909ec
Fixed most rubocop issues (#18048)
* Fixed most rubocop issues

* Fixed accidental schema changes

* Fixed Vault specs
2022-07-07 14:36:08 -06:00
Jeremy Friesen
7a4dd3286d
Adding VariantQuery reseed randomization option (#17942)
Prior to this change, we setup our feed variant query to have a cached
randomization seed.  For a given user this seed is cached for 15
minutes.  The goal of this is to provide mostly consistent sort orders
on the feed articles during that 15 minute window.

With this change, we allow for a feed variant to say ignore the cached
seed and generate a new one for this call.

I have also added explicit settings to each of the feed variants to
reflect our desired intentions for those features.

Relates to

- forem/forem#17833
- forem/forem#17826

Closes forem/forem#17940
2022-06-16 15:04:08 -04:00
Jeremy Friesen
7ca69435ab
Adding caching to our feed's random seed (#17833)
Prior to this commit, we were always using a new random number for a
query.  With this commit we cache the randomized seed; which allows us
to have "a repeatable random" experience within a windo of time.

Closes forem/forem#17826
2022-06-07 12:20:18 -04:00
Jeremy Friesen
2e19ea9255
Adding ability to "seed" feed's SQL randomization (#17827)
The commit includes three changes that work to allow us to "seed" the
randomization.  When using the same seed, and randomizer should/must
return the same sequence of numbers.

First, I added the "seed" parameter to the variant query.  If you want
the same sequence of random numbers from query to query, pass the same
seed.  Otherwise, we'll use a Ruby generated random seed.

Second, I added a virtual column to the virtual `article_relevancies`
table.  If you provide a seed, and call the query twice, the first row
in each of `article_relevancies` will have the same `randomized_value`,
likewise the second row, etc.

Third, I amended the existing `order_by_lever` that had a `RANDOM()`
postgresql function call.  I replaced that with the `randomized_value`
which is computed based on the provided seed.

Fundamentally the idea is to allow for randomness but introduce possible
repeatability; a hard thing considering that some of the inner selection
criteria is not fixed (e.g. number of reactions can change from moment
to moment).

I wrote a [complimentary blog post][1] to further explain what's
happening.

Related to forem/forem#17826

[1]:https://takeonrules.com/2022/06/03/adding-reproducible-randomization-to-sql-queries/
2022-06-06 11:58:16 -04:00
Jeremy Friesen
3fb085b3d9
Adding more context to /admin/abtests (#17821)
* Adding more context to `/admin/abtests`

Prior to this commit, the `/admin/abtests` was fully rendered by the
field_test gem (see
https://github.com/ankane/field_test/blob/master/app/views/field_test/experiments/index.html.erb
and
https://github.com/ankane/field_test/blob/master/app/views/field_test/experiments/show.html.erb).

With this commit, we're pre-pending our own
partial (e.g. `app/views/field_test/experiments/_experiments.html.erb`)
in the view paths; which means instead of rendering
https://github.com/ankane/field_test/blob/master/app/views/field_test/experiments/_experiments.html.erb
we render our newly created
`app/views/field_test/experiments/_experiments.html.erb`)

Why the ugly antics in the view?  As I'm not fully certain if this will
"meet" the full needs of those monitoring the experiments.  I'm also
constructing this to most closely match the spreadsheet that has been
assembled for tracking this information.

Why introduce another caching layer for the variant assembly?  The
original caching
layer (e.g. `Articles::Feeds::VariantAssembler.pre_assembled_variants`)
is in place to provide the quickest access to the assembled variants.
But due to the nature of the implemented view, we are needing a
programmatic representation of all variants.  And the added Rails cache
is there to minimize disk reads.

Closes forem/forem#17820

* Update app/models/articles/feeds/variant_assembler.rb

Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>

* Update app/models/articles/feeds/variant_assembler.rb

Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>

Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>
2022-06-03 11:27:09 -04:00
Jeremy Friesen
38ee9ef309
Refactoring so relevancy levers define expected variables (#17591)
Prior to this commit, the SQL fragments included variables that were set
configured at a global level.

With this commit, we're now saying that each lever "knows" what variable
it needs; and providing the means at lever declaration time to "say"
what those variable names are.  (e.g. `Articles::Feeds::LEVER_CATALOG`).

Then as part of the variant configuration (in the
`./config/feed-variants/*.json` files) we now include the expected value
of those parameters; which by convention (and coercion) are integers.

This relates to forem/forem#17584 because we want to move from a
privileged user reaction that has two values (`negative` and `positive`)
into four values (`very_negative`, `negative`, `positive`, and
`very_positive`).  To do that, we'll create a new lever; but that's for
another pull request.
2022-05-06 13:54:33 -04:00
Jeremy Friesen
b808843564
Updating documentation for the relevancy feed (#17503)
* Updating documentation for the relevancy feed

This looks to remove the chatter of the document and instead point folks
to locations within the code.

Closes forem/forem#17245

* Update app/models/articles/feeds/README.md

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>

* Update app/models/articles/feeds/README.md

Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>

* Update app/models/articles/feeds/README.md

Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>

* Update app/models/articles/feeds/README.md

Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>

* Update app/models/articles/feeds/README.md

Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>

* Update app/models/articles/feeds/README.md

Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>

* Update app/models/articles/feeds/README.md

Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>

* Update app/models/articles/feeds/README.md

Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>
2022-04-29 09:50:05 -04:00
Jeremy Friesen
4bc181503f
Removing WeightedQueryStrategy; use VariantQuery now (#17420)
You had a good ride, but your successor, the
`Articles::Feeds::VariantQuery`, is doing well in production.  And has
been for quite awhile.

You helped us get to a better spot in regards to the feed algorithm, but
your time has come to rest as a memory in our git history.
2022-04-27 09:19:09 -04:00
Jeremy Friesen
8b37e3aca9
Tidying up the Articles::Feeds::VariantQuery (#17396)
This commit does three things:

1.  Breaks the inheritance of the VariantQuery.
2.  Revisits the in-line documentation to better reflect current state.
3.  Removes branching logic that we don't need.

My goal is to have a VariantQuery that is what we need, conforms to the
method interface of WeightedQueryStrategy but does not rely on it.

After we merge this, we can observe if things break on DEV and fallback
to WeightedQueryStrategy if this doesn't work.  Once the
`Articles::Feeds::VariantQuery` has fledged on it's own, we can remove
the feature flag and then finally remove the weighted query strategy.

But that's the future steps.

Related to forem/forem#17393
2022-04-22 15:26:37 -04:00
Jeremy Friesen
0d0464be2f
Allowing VariantQuery for Feed Generation (#17382)
* Allowing VariantQuery for Feed Generation

Apologies for the breadth of this pull request, I had considered many
small commits, but felt that would've been more effort for the value
provided.

This commit includes the following:

- Documentation updates to the feed variant (though not the final pass)
- Renaming and adding RelevancyLevers that help differentiate
- Adding RelevancyLever#range to provide documentation
- Reducing redundent controller logic by making a
  `Articles::Feeds.feed_for` method.
- Adding some configuration validation for RelevancyLevers
- Adding constants for better clarification
- Testing unhappy paths for feed configuration
- Adjusting the module namespace of some objects
- Exposing top-level configurations for variants (along with their
  defaults)
- Creating the VariantyQuery that at present inherits from the
  `Articles::Feeds::WeightedQueryStrategy`

As implemented, we can deploy this code to production without using the
new VariantQuery.  Once we toggle on the
`:feed_uses_variant_query_feature` FeatureFlag, it will switch to using
the VariantQuery.  The VariantQuery's two variants and the
internal configuration of `Articles::Feeds::WeightedQueryStrategy`
produce the same query.

The goal of this factor is to allow for a quick on and off toggle of the
feed query; to ensure that what we introduce remains performant.

- Closes forem/forem#17272
- Closes forem/forem#17276
- Closes forem/forem#17216

In addition, I will be recording a code-walkthrough and linking that
recording to the pull request.

* Apply suggestions from code review

Co-authored-by: Mac Siri <krairit.siri@gmail.com>

Co-authored-by: Mac Siri <krairit.siri@gmail.com>
2022-04-21 11:07:09 -04:00
Jeremy Friesen
aebde42730
Adjusting feed query to establish a new incumbent (#17308)
This commit modifies two things:

1.  The sort order of the feed.
2.  Locking in the next incumbent for the feed.

In consultation with Jamie regarding the SQL, I've switched from a WHERE
IN type clause to creating a JOIN.  This allows for the calculated
relevancy score to become a sortable value for the feed.

Second the incumbent feed is a slight modification of the past winner;
based on the results of a long running experiment.  This adjustment is
per conversations with product.

The past experiment ran since early February, so it's time to retire it,
regroup and move forward.

Closes forem/forem#17307
2022-04-19 09:10:00 -04:00
Monica Mateiu
6818ef3ed0
Remove references to article.spaminess_rating (#17235)
* Remove references to article.spaminess

* Update app/models/article.rb

* Update spec/workers/comments/calculate_score_worker_spec.rb

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>

* remove unnecessary BlackBox spec

* Update spec/workers/comments/calculate_score_worker_spec.rb

Co-authored-by: Jamie Gaskins <jgaskins@hey.com>

* Remove references to calculate_spaminess

* Update app/workers/comments/calculate_score_worker.rb

Co-authored-by: Jamie Gaskins <jgaskins@hey.com>

Co-authored-by: Jeremy Friesen <jeremy.n.friesen@gmail.com>
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
Co-authored-by: Jamie Gaskins <jgaskins@hey.com>
Co-authored-by: Jamie Gaskins <jamie@forem.com>
2022-04-15 11:43:48 -04:00
Jeremy Friesen
f8590a6d84
Correcting public_reactions_count (#17280)
Prior to this commit, we were looking at trusted user's reaction
counts.  This is not the desired column to be testing.

See
f1d6291d00/app/models/reaction.rb (L25-L28)

```ruby
  counter_culture :reactable,
                  column_name: proc { |model|
                    PUBLIC_CATEGORIES.include?(model.category) ? "public_reactions_count" : "reactions_count"
                  }
```

In the above code, all public reactions (hearts, unicorns, reading
list) go to public_reactions_count.  Other things go to the
`reactions_count`; which is the thumbs up and thumbs down.

Fixes forem/forem#17260
2022-04-14 13:31:07 -04:00
Ridhwana
1025900d16
Improve the UX of the SMTP Section (#16223)
* feat: allow reply_to and email_from to be set for an SMTP config

* feat: use these SMTP values in the mailers

* spec: test the application_mailer

* fix: validate with email, and not url

* feat: mimic macs changes from https://github.com/forem/forem/pull/16216 to use in this PR

* setup packs for admin

* refactor: order the keys and add a const for the auth methods

* feat: rename the header to a more user friendlly name

* chore: move the section with Emails

* feat: add a toggle that will show and hide the SMTP form under certain conditions

* feat: add the javaScript to handle the toggles

* feat: add a better description until we convert to a dropdown

* feat: ensure that we have declared sendgrid_enabled

* chore: add  anote to the config controller

* chore: remove references of the email addresses to keep brnach scoped

* feat: tweak js

* test: cypress workflow to update smtp settings

* feat : update the smtp tests

* remove comments

* update test

* chore: rename NOTE

* feat: polisha dn test ForemInstance.only_sendgrid_enabled?

* chore: remove specs

* Update app/lib/constants/settings/smtp.rb

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>

* Update spec/system/admin/config/admin_updates_smtp_settings_spec.rb

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>

* Update spec/system/admin/config/admin_updates_smtp_settings_spec.rb

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>

* Update spec/system/admin/config/admin_updates_smtp_settings_spec.rb

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>

* Update app/javascript/packs/admin/config/smtp.js

Co-authored-by: Nick Taylor <nick@iamdeveloper.com>

* refactor js as per comments

* refactor as per comments

* Update cypress/integration/seededFlows/adminFlows/config/emailServerSettingsSection.spec.js

Co-authored-by: Suzanne Aitchison <suzanne@forem.com>

* refactor: update the Cypress tests

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
Co-authored-by: Nick Taylor <nick@iamdeveloper.com>
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
2022-02-15 18:40:14 +02:00
Ben Halpern
84379574d5
Feed experiment 6: recency (#16438)
* Feed experiment 6: recency

* Update app/services/articles/feeds/weighted_query_strategy.rb

Co-authored-by: Michael Kohl <me@citizen428.net>

* Update app/services/articles/feeds/weighted_query_strategy.rb

Co-authored-by: Michael Kohl <me@citizen428.net>

Co-authored-by: Michael Kohl <me@citizen428.net>
2022-02-08 15:01:18 -05:00
Daniel Uber
9f79c92563
Cap tag point sum to 10 (#16342)
* Cap tag point sum to 10

We only have 0..9 configured for cases to consider, treat all "large"
numbers as 10, since it will follow the same path in the weighting.

Use least(value, ...) to cap this. See https://www.postgresql.org/docs/11/functions-conditional.html#FUNCTIONS-GREATEST-LEAST

Fixes https://github.com/forem/forem/issues/16341

* UPCASE sql keywords to distinguish from column or table names
2022-01-27 16:22:49 -06:00
Ben Halpern
f393de4c21
Feed Experiment round 5: Factor in sum of tag points (#16272)
* Experiment with sum of tag points

* Cast sum of points to integer
2022-01-25 17:08:31 -05:00
Daniel Uber
394d33e134
Select max from a subquery of two things (#16177)
* Select max from a subquery of two things

max(int, int) not a function, says postgres, once it stopped yelling
about parenthesis balancing.

* prefer greatest to select max from subquery
2022-01-18 18:21:14 -05:00
Jeremy Friesen
06f6573436
Ensuring that we don't divide by zero (#16172)
* Ensuring that we don't divide by zero

Adding one to a "always-ish greater than or equal to 0" value.  This
should resolve a flakey test and a seemingly erratic production error.

Resolves https://app.honeybadger.io/projects/66984/faults/83596547

* Favoring min over adding 1

Due to syncrhonization antics, let's not assume time is consistent
across servers.

Timey Whimey Wibbley Wobbley

* Update app/services/articles/feeds/weighted_query_strategy.rb

Co-authored-by: Daniel Uber <djuber@gmail.com>

Co-authored-by: Daniel Uber <djuber@gmail.com>
2022-01-18 17:25:15 -05:00
Julianna Tetreault
46d40b2f54
Resolves rubocop violations in weighted_query_strategy.rb (#16164) 2022-01-18 09:11:41 -07:00
Jeremy Friesen
5534a8fa18
Addressing rubocop violations (#16156)
```shell
❯ bundle exec rubocop -A
Inspecting 1856 files

Offenses:

app/controllers/admin/settings/mandatory_settings_controller.rb:17:57: C: [Corrected] Rails/CompactBlank: Use compact_blank instead.
            settings_model.public_send("#{key}=", value.reject(&:blank?)) if value.present?
                                                        ^^^^^^^^^^^^^^^^
app/controllers/users_controller.rb:66:58: C: [Corrected] Rails/CompactBlank: Use compact_blank instead.
      Honeycomb.add_field("error", @user.errors.messages.reject { |_, v| v.empty? })
                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
app/controllers/users_controller.rb:280:58: C: [Corrected] Rails/CompactBlank: Use compact_blank instead.
      Honeycomb.add_field("error", @user.errors.messages.reject { |_, v| v.empty? })
                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
app/models/settings/base.rb:111:54: C: [Corrected] Rails/CompactBlank: Use compact_blank instead.
          value.split(separator || SEPARATOR_REGEXP).reject(&:empty?).map(&:strip)
                                                     ^^^^^^^^^^^^^^^^
app/services/articles/feeds/weighted_query_strategy.rb:269:121: C: Layout/LineLength: Line is too long. [126/120] (https://rubystyle.guide#max-line-length)
      def initialize(user: nil, number_of_articles: 50, page: 1, tag: nil, strategy: AbExperiment::ORIGINAL_VARIANT, **config)
                                                                                                                        ^^^^^^
app/services/images/optimizer.rb:27:50: C: [Corrected] Rails/CompactBlank: Use compact_blank instead.
      options = DEFAULT_CL_OPTIONS.merge(kwargs).reject { |_, v| v.blank? }
                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
app/services/images/optimizer.rb:46:68: C: [Corrected] Rails/CompactBlank: Use compact_blank instead.
      options = DEFAULT_IMGPROXY_OPTIONS.merge(translated_options).reject { |_, v| v.blank? }
                                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
app/services/settings/upsert.rb:30:55: C: [Corrected] Rails/CompactBlank: Use compact_blank instead.
          settings_class.public_send("#{key}=", value.reject(&:blank?))
                                                      ^^^^^^^^^^^^^^^^

1856 files inspected, 8 offenses detected, 7 offenses corrected
```

After this commit:

```shell
❯ bundle exec rubocop
Inspecting 1856 files

1856 files inspected, no offenses detected
```

1856 files inspected, no offenses detected
2022-01-17 17:21:06 -05:00
Ben Halpern
8159f4383c
Feed experiment 4: Final order (#16128)
* Feed experiment 4: Final order

* Remove development line

* Fix text in tests

* Fix text in tests

* Switch query to greatest of
2022-01-17 17:05:43 -05:00
Jeremy Friesen
02b3bb8a8e
Extracting a not_authored_by scope (#16123)
In my quest to find `where(attribute: value)` in controllers and even
services, I stumbled upon this pattern.

As of this commit (and prior) an Article's associated user is it's author.
2022-01-17 15:20:49 -05:00
Ben Halpern
5a033fcce3
Turn featured back on as query scoring weight (#16093) 2022-01-14 07:32:52 -05:00
Ben Halpern
294b3471af
Proposing feed experiment 3: More comment count weight (#15993)
* Initial work

* Implement strategy injection into WeightedQueryStrategy

* Modify field_test config and adding variants

* Change orginal to constant and make some other adjustments

* Fix hardcoded test values
2022-01-11 11:05:06 -05:00
Jeremy Friesen
6269aa4108
Proposing a new feed experiment (#15789)
* Proposing a new feed experiment

This commit involves tweaking a few subtle aspects of the challenger:

1) Remove the sort by published date on the relevance score.  The
   articles will now be listed in the order of perceived relevance.
2) Disable a few levers that did little.  There really aren't any
   `articles.featured = true` articles in the database.
3) Gently increase the weight of each comment in a linear manner.
4) Give a little more weight to posts that don't have followed tags.

In addition, it involves renaming the conversions to better convey their
implementation.  This renaming helps create more descriptive labels for
the test results at `/admin/abtests`.

This commit also adds logic to repurpose the AbExperiment feed_strategy
logic; I envision adjusting the weighted feed strategy levers with some
frequency.

Per discussions, I'm also disabling the "not logged in" feed testing.
We'll use the LargeForemExperimental for this.

* Flipping attribute name to positive

Mentally "not disabled" is harder to parse than "enabled".  This change
helps with setting an optimistic attribute.

* Bump for travis

* Bump for travis

* Removing file committed by mistake

* Extracting method
2022-01-03 14:38:14 -05:00
Jeremy Friesen
f2cf46d426
Refactoring to leverage an Article scope (#15850)
* Refactoring to leverage an Article scope

I've been looking at the queries that involve filtering articles based
on scores.  There's several places that seem to be close to consistent,
but have some nuanced difference.

This refactor consolidates one of those "almost the same" cases.

* Bump for travis
2021-12-29 09:25:31 -05:00
Josh Soref
0403e78f08
Spelling (#15702)
* spelling: access

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: additional

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: administrative

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: aggregate

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: assigns

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: attributes

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: autocomplete

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: because

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: between

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: bootstrap

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: calculating

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: captcha

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: character

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: chosen

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: commenter

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: competitor

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: componentize

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: contrast

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: corresponding

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: description

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: destroyed

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: destroys

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: discussion

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: episode

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: escaped

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: evaluates

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: expired

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: explicitly

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: facebook

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: fragment

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: functionality

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: improper

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: incentive

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: interfere

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: latest

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: message

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: minimum

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: moderator

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: mouseover

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: mutual

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: nonexistent

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: notification

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: occasionally

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: occurrence

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: occurs

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: octokit

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: offset

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: omitted

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: opacity

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: organization

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: organizations

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: overridden

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: override

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: overriding

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: prefill

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: previous

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: profile

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: recycling

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: registered

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: repositories

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: rescuing

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: response

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: returns

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: second

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: separator

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: services

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: subscriber

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: subscription

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: success

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: successful

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: successfully

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: suppress

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: test

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: thought

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: uniqueness

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: unknown

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: unproductive

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: unreachable

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: unsuccessful

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: utilities

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: utility

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: valid

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: voluntarily

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: vomited

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: website

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

* spelling: withholding

Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>

Co-authored-by: Josh Soref <jsoref@users.noreply.github.com>
2021-12-07 06:59:10 -07:00
Jeremy Friesen
8c126ca2e0
Fixing blank home page for anonymous/visitor (#15665)
Prior to this fix, in the production rails console we ran the following:

```ruby
Articles::Feeds::WeightedQueryStrategy
  .new(user: nil, page: 1, tags: nil)
  .featured_story_and_default_home_feed(user_signed_in: false)
```

The result was: `[nil, []]`.  Which in the
[StoriesController#assign_feed_stories][1] is where we have the
following:

```ruby
@featured_story, @stories = feed
  .featured_story_and_default_home_feed(
    user_signed_in: user_signed_in?)
```

In Blazer, I ran the following query at 2021-12-02 Thu 20:41 EST:

```sql
SELECT * FROM articles
WHERE featured = true
AND published = true
AND published_at > (NOW() - interval '7 days')
AND published_at < NOW()
AND main_image IS NOT NULL
```

So the logic of filtering with `only_featured` resulted in the
`@featured_story` being `nil`.  Which then cascaded into the logic for
determining the remainder of the results.

For further discussion see the inline comments and specs that I wrote.

In addition to the above, I'm also reusing
`Articles::Feeds::FindFeaturedStory`

Prior to this commit, the `Articles::Feeds::WeightedQueryStrategy` first
grabbed the featured story, then grabbed the remaining articles.  This
is different than the implementation of the
`Articles::Feeds::LargeForemExperimental` in which we first grab all the
articles then claim one as the featured.

In the WeightedQueryStrategy, the articles would not include the
featured.  However, in the LargeForemExperimental it would.  This
results in upstream logic in [app/javascript/articles/Feed.jsx][2]
going through and removing one of the articles.

[1]:c1a3ba99eb/app/controllers/stories_controller.rb (L237-L271)
[2]:c1a3ba99eb/app/javascript/articles/Feed.jsx (L57-L63)
2021-12-03 08:32:02 -05:00
Jeremy Friesen
6d1268f19b
Fixing logic error for feed (#15611)
* Fixing logic error for feed

Prior to this change, we were forcing features into the feed.

* Adding spec to help ensure parameter match

Prior to this commit, but before I had merged changes into main, the I
had implemented `alias default_home_feed call`.  However, this broke the
interface assumed in the feeds controller.
2021-12-01 13:48:02 -05:00
Jeremy Friesen
3860f5c7b5
Extracting Article.featured scope (#15612)
In addition adding commentary on the oddly named
`Articles::Feeds::FindFeaturedStory` class given that it never filters
on "featured".
2021-12-01 12:16:32 -05:00
Jeremy Friesen
60dc2cc12e
Fixing weighted query hotness grab logic (#15528)
* Fixing weighted query hotness grab logic

Prior to this commit, I carried over (albeit imprecisely) logic from the
LargeForemExperimental.  That logic was to help limit articles to those
that were published since the user's latest page view.

However, I introduced a bug in this transcription.  Now both
LargeForemExperimental and WeightedQueryStrategy use the same logic to
determine the oldest publication date to search for in the feed.

This resolves a bug reported where users were not seeing a large number
of items in their feed.

Incidentally, if a forem has little activity in the 18 hours, there
might be very few items in the feed.

I believe, going forward, we may need to better parameterize how many
hours is considered "stale since last page view".

Related to #15240

* Fixing implementation detail

* Extracting helper method

Prior to this commit, I had introduced a method an put it in a less
ideal module space.  This commit extracts that method to a more readily
shareable module space.

I've added a few more specs to help clarify and verify behavior.

* Updating documentation

* Renaming and documenting variables/constants

* Fixing that which I broke
2021-11-30 12:55:48 -05:00
Jeremy Friesen
a9823e001f
Limiting feed queries (#15523)
* Limiting feed queries

Prior to this commit, we were joining on all comments.  With this
change, we're limiting the comments to reflect the same time period as
our article limit.

Hopefully this further improves the performance of the query.

* Removing unintentional puts statement
2021-11-30 08:02:25 -05:00
Jeremy Friesen
816e92e549
Configurable weighted feed strategy (#15240)
* AB Test for Feed Strategy

Adding the plumbing for performing AB Tests on the feed strategy.

You can add the environment variable `AB_TEST_SERVICE_FEED_STRATEGY` to
specify the field test to run.

For example, from the forem application home directory you can run the
following to force a strategy for all users:

```console
AB_TEST_SERVICE_FEED_STRATEGY=original bin/start
```

Forcing the strategy can significantly help with quality assurance testing.

* Adding configurable weighted feed strategy

I wrote about this exploration on [DEV.to][1].  This particular
implementation provides significant configuration options for A/B
testing.

At it's core this `Articles::Feeds::WeightedQueryStrategy` provides a
means to query articles that are relevant to the user.  The
configuration helps us test and refine those values.

Further more this implementation introduces the idea of some common
interfaces for feed strategies.  I'm not happy with the naming
convention but we can work to change that.

This even hints at a mechanism for administrators to configure.

Related to forem/rfcs#314, as I work towards understanding the feed
algorithm. And #15180

[1]:https://dev.to/jeremyf/practicing-postgresql-and-postulating-improvements-5m

* Renaming AbTestService to AbExperiment

I'm loath to end any production class's filename with "test".  I don't
want minitest or rspec coming along and attempting to "test" that file.
In addition, I find that Experiment better describes the concept.

Also, per conversations with Michael K, we favor nouns objects to go
in app/models and verb objects to go in app/services.  That's a great
separation.

* Factoring AbExperiment into a Controller concern

I wish this could be a stand-alone module but the implementation of
field test has cases that require controller context.

* Sanitizing configurable weighted strategy cases

* Minor refactoring of coding structure

* Renaming parameter for clarity

* Renaming variable to avoid confusion

Prior to the commit, `env` is a controller variable.  The injected `env`
variable didn't collide, but did create just a bit of confusion.

With this change, I'm trying to disentangle from that variable.

* Factoring AbExperiment away from mixin concern

Per discussion with Michael K, we both agree that we do not like
controller concern mixins.  They can quickly complicate the legibility
of any given codebase.

This refactor provides a common and simplified interface for a Plain Old
Ruby Object (PORO) that accounts for the implementation considerations
of the underlying "field_test" controller method.

* Adding Datadog.tracer.trace around feed actions

* Adding featured_article_factor

Prior to this commit, there was no consideration for how moderators
engage with an article.  This is a first nudge towards that effort.

* Removing spec

Add a spec that says yes or no we're feature testing something seems
fragile.  Namely, why are we wanting to assert that as a truism.

* Helping ensure we're "tracing" the same things

From the code comment:

> Hey, why the to_a you say?  Because the
> LargeForemExperimental has already done this.  But the
> weighted strategy has not.  I also don't want to alter the
> weighted query implementation as it returns a lovely
> ActiveRecord::Relation.  So this is a concession.

* Addressing parameters for tracing function

* For weight feed query only count positive tags

A user assigns explicit points to the tags that they do or do not want
to see.  This change treats the negative explicit_points as an indicator
that they don't want to see the tag.

* Adding weighted factor for privileged user reactions

* Updating comments to better clarify

* Adding additional constraint for published_at

* Updating documentation and adding logger

* Adding documentation concerning method call

* Upping logging level for experiments

See conversations in https://github.com/forem/forem/pull/15240

* Adding group by clauses to appease postgres configs

* Injecting relevant scoring method's joins

Prior to this commit, we included all of the joins regardless of the
scoring methods chosen.  With this commit, we only inject joins that are
used with the commit.

What this would allow us to do is to then have some pre-cooked scoring
methods but not turn them on.  Or to turn off the more expensive scoring
methods.

* Adding feature flag for feed strategy experiment
2021-11-29 10:46:56 -05:00
Jeremy Friesen
7ba0b49ea8
Parameterizing featured story requiring main image (#15333)
* Parameterizing featured story requiring main image

This begins to ease the resolution of #15292.

If we merge #15240, I could see setting an `Article` constant for this
value and allowing the administrator to choose the particular behavior.

Once we have insight from the product team, we can move forward with a
more comprehensive solution.

* Adding default parameter

While this is an `@api private` method, I am aligning the defaults with
it's `@api public` caller.  Yes, only specs call the method, but I'd
rather not fiddle with the specs at this moment in time.

* Reworking logic to be more scannable
2021-11-18 16:28:58 -05:00
Jeremy Friesen
f5243ea844
Normalizing feed page size (#15326)
This commit removes a handful of magic numbers and instead relies on a
constant.

This has a small impact in that the Basic feed will now return 50
articles instead of 25.  However, normalizing the feed pagination window
size helps reduce some oddities in reporting.

In addition, this might be something we consider giving administrators
the ability to set (with default options, because we shouldn't allow
page sizes of 10_000 as that's a massive memory hog).

Related to #14709
2021-11-16 11:12:01 -05:00
Jeremy Friesen
da36b5fd17
Refactoring to extract common interface (#15229)
This is a hypothetical and experimental interface change.  The goal of
the change is to begin looking at what a "Feed"'s method interface would
look like.

I would like to reduce the controller logic necessary for deciding on
different strategies, and instead defer to a builder/strategy lookup
pattern.
2021-11-02 13:16:09 -04:00
Jeremy Friesen
ed78f8f479
Extracting a common calculator for feed weights (#15199)
Prior to this commit, there existed duplicate logic between two of the
primary user feeds.  This refactor introduces a new object which is
solely concerned with calculating scores to add to an article base score.

With this commit, we can introduce A/B testing by changing passing
different config values to `ArticleScoreCalculatorForUser.new`.

My suspicion is that for some of this, we might be able to better
leverage the database via select statements and SQL sums.  However, that
is presently outside of what I'm prepared to tackle.
2021-10-27 04:45:53 -04:00
Jeremy Friesen
7989226d3f
Jeremyf/refactor order of operations (#15192)
* Adding guard clause for nill org feed score

There's no sense running a query if we don't have an organization.  This
is a small refactor that might offer a most nominal speed bump

* Caching calculated value

Prior to this commit, we calculated the user_experience_level once per
article in the feed.

This change adds a cache, creating a minor computational improvement.
2021-10-26 10:18:52 -04:00
Jeremy Friesen
fbce226c08
Low Stakes Refactor of Feed class (#15180)
* Removing state change and unused method

Prior to this commit, the `@comment_weight` value would change.  This is
not ideal as depending on the call sequence, can notably change the
output.  I suspect this state change occurred so as to not alter an
underlying spec.

What this change does is remove the state change, removes a dead method,
renames a method (to the dead method name), and leverages
parameterization to better test a spec that was brittle based on
possible state changes.

I believe, from a logical stand point, that this change does not impact
the functionality nor the actual logic that is part of the production
call path.

* Marking methods as @api private

The goal is to highlight that we really shouldn't be calling these
outside of their contained class.  Ideally, I'd love to make them
private methods, but there are specs and would prefer to not use
`__send__` to change those specs.

This is a noop change.

* Renaming method to refelct returned param order

Prior to this commit the "default_home_feed_and_featured_story" returned
an array of `[featured_story, default_home_feed]`.  The method name and
the order of the returned values were misaligned.  This change helps
align the method name and the order of those returned values.

Note, methods such as `each_with_index` have an `element, index`
parameter order.
2021-10-26 09:57:33 -04:00
Jeremy Friesen
24929ba20d
Naming "magic numbers" to help clarify (#15161)
As part of my reading and seeking to understand the
`Articles::Feed::LargeForemExperimental` class, I figured I would come
in and add parameters.  In adding parameters, one thought I had was that
we can more readily create a data structure that allows us to tweak the
various implicit values.

This is a first step to help me understand and name the numbers.
2021-10-22 12:37:11 -04:00