* 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
* 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
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
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.
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.
* Initial work to expand detection functionality
* Finish up functionality and tests
* Fix class name
* Fix tests
* Update spec/services/articles/enrich_image_attributes_spec.rb
Co-authored-by: Michael Kohl <citizen428@forem.com>
* Update spec/services/articles/enrich_image_attributes_spec.rb
Co-authored-by: Michael Kohl <citizen428@forem.com>
* 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.
* 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.
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.
* Start cleaning up feed code
* Move published_articles_by_tag to service
* Add timeframe and latest feed services
* fixup! Move published_articles_by_tag to service
* Move constant to correct location
* Fix spec
* schema file undelete description
* feat: v1 of the script
* Flesh out remaining enums under their categories
* complete UsersSettings data update script
* complete DUS for relevant attributes in users and profiles tables
* complete DUS for users_notification_settings
* alphabetize user_settings sql file
* safeguard against null values for "null: false" settings
* Set up actual UsersSettings DUS and specs files
* fix broken DUS script
* complete specs for UsersSetting DUS
* Address QA of specs
* complete specs for users_notification_settings DUS
* fix the typos (thanks Julianna!)
* begin implementation
* still building
* add missing attribute "email_membership_newsletter"
* complete sync code (except race condition for user profile)
* complete implementation, remains tests
* Address PR review and fix Travis fails
* remove superfluous Profile.new
* fix travis fails
* feat: update the users_notification_setting attributes from the user model
* feat: use the config fonts enums to display the fonts
* feat: loop through the keys
* fix profile = nil blowing up; add specs for notification_setting model
* remove unneeded spec
* remove feed validation until after sync code removed; fixes feed_import spec failures
* remove spec associated with feed_url validation in user_setting model
* fix failing spec 😅
* add TODO
* feat: set the user settings in the user controller and use it in the customization form
* feat: move some update logic to the users settings controller thats being used from customization
* feat: show the updated values form the users_settingd and not the user instance
* Generalize redirect back to current tab
* still trying to reflect changed theme upon refresh
* customizations take effect on refresh
* remove 'with_feed' scope from user model
Co-authored-by: Jamie Gaskins <jamie@forem.com>
* start with takeover for fields previously in profiles table
* Takeover code for `publishing_from_rss` section in Settings (#13914)
* implement takeover code part 1
* implement takeover code
* fix feed fetch
* need rhymes help
* complete implementation; specs pending
* fix STUPID omission that caused so many headaches 😫
* implement profile fields pointing to users_settings 🎉
* run migrations
* implement inbox type & guidelines takeover code; specs pending (#13911)
* Point changes in notification settings to `users_notification_settings` table (#13910)
* implement takeover code; remains specs
* address PR feedback; remove related sync code
* address PR review feedback
* need help with routing and specs
* address pr review
* addressing pr review
* Treat implementation edge cases and omissions 😅
* fix uncommented comment
* fixing implementation cases
* address more PR review feedback
* fixing notifications use-cases
* refactor settings controller
* more pr review changes
* solving bugs
* fix broken onboarding
* handle eperience_level calls
* more fixes
* remove unneeded mappings
* add To-dos for quety updates
* remove done TODO
* purge done TODOs
* update notification_settings-related queries
* start fixing specs
* fixing specs
* fix notification and lrg_forem specs
* fixing broken specs
* still fixing
* fix line dif and remove reloads from user.rb
* run specs
* silence bullet and other fixes
* remove setting migration scripts and specs, fix more settings for specs
* handle missing user for article builder and fix notification specs
* fix some final controller specs and re-add incorrectly removed specs
* remove deprecated data update scripts and related workers, put travis back
* refactor admin tags mods controller, write/move specs for users notifications settings controller
* schema cleanup and other small refactors for consistency
* set field we can invalidate in spec via active record instead of at the db level
* remove I think an uneccessary hook call from subscribe_to_mailchimp_newsletter
* use bnefore_create to setup settings, please dont blow up the test suite
* mailchimp bot fix
* remove decorator in favor of single model method
Co-authored-by: Arit Amana <msarit@gmail.com>
Co-authored-by: Ridhwana <ridhwana.khan16@gmail.com>
Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>
Co-authored-by: Jamie Gaskins <jamie@forem.com>
* Add inline animated image detection
* Do not detect animation during preview
* Promote FastImage as a dependency
* Add Articles::DetectAnimatedImages service
* Restore previous parsing
* Add and use Articles::DetectAnimatedImagesWorker
* Remove obsolete poc
* Properly detect images in the after commit callback
* Simplify logic
* Use a second guard clause
* Fix parsing of relative paths and add tests
* Have Articles::DetectAnimatedImages correctly detect relative images uploaded locally
* Change Articles::DetectAnimatedImagesWorker priority to medium
* Only one & required
* Initial work for @-mention notifications from posts
* Revert article.published changes to article updater, add clarifying comments
* Extract article preview into reusable partial for notification views
* Clean up Article Updater
* Address + remove some FIXMEs
* Add a whole buncha specs for @-mention functionality in posts YAY
* Refactor create all spec to use shared examples, add clarifying comments
* Add guard clause to create all service
* Update new mention and notifiable action specs
* Some additional cleanup
* Add specs + shared examples to SendEmailNotificationWorker spec
* Use aggregate_failures where applicable
Co-authored-by: Michael Kohl <citizen428@dev.to>
* Cleanup and address code review comments
* Add MentionDecorator + relevant specs
* Address comments/issues flagged by @rhymes
* Optimize plucking user_ids when checking for article followers
Co-authored-by: Michael Kohl <citizen428@dev.to>
* Make definition of new user configurable
* Add new user configuration to config admin
* Only decorate on-demand
* Adapt new user logic
* Update app/lib/constants/site_config.rb
Co-authored-by: Vaidehi Joshi <vaidehi.sj@gmail.com>
Co-authored-by: Vaidehi Joshi <vaidehi.sj@gmail.com>
* Start refactoring articles update
* Articles update refactoring
* Added a couple of tests for Articles::Updater
* Reorganized code in Articles::Updater and ::Attributes a bit
* A couple of more tests for Articles::Attributes
* Reverted changed in the articles_controller
* Edited_at in Articles::Attributes
* Use Articles::Updater in ArticlesController
* Tests for notifications when published/unpublished an article
* Removed old code
* Return result object from Articles::Updater
* Don't reset articles collection when no series was passed
* Fixed sending notifications when published an article the first time
* Fix articles collection when updating
* Fix setting attributes for articles update
* Spec for articles update via api failure
* More tests for Articles::Updater
* More specs for articles updating
* Pass article instead of article_id to articles updater to avoid loading an article twice
* Remove the redundant check in the articles_controller
* Articles::Attributes refactoring
* Declare winner in feed_top_articles_query test
* Some WIP modifications to lfe
* Fix up tests and generalize
* Fix style
* Adjust tests to handle different field test scenarios
* Remove socre_randomness tests
* Fix test to fit stub
* Update app/workers/users/record_field_test_event_worker.rb
When `acts-as-taggable-on`'s `.tagged_with()` is used with `any: true`,
the gem will use `SELECT *` regardless of any previous (or following) requests
of selecting a limited amount of columns.
Given that the `articles` table has [73 columns](https://dev.to/admin/blazer/queries/314-number-of-columns-in-all-tables)
that will amount to wasted RAM memory for columns we don't need.
By "unscoping" any previous `select()` we can optimize used memory.
Before:
```ruby
[24] pry(main)> Article.tagged_with([:ruby], any: true).select(:id, :name).to_sql
=> "SELECT \"articles\".*, \"articles\".\"id\", \"name\" FROM \"articles\" WHERE EXISTS (SELECT * FROM \"taggings\" WHERE \"taggings\".\"taggable_id\" = \"articles\".\"id\" AND \"taggings\".\"taggable_type\" = 'Article' AND \"taggings\".\"tag_id\" IN (SELECT \"tags\".\"id\" FROM \"tags\" WHERE (\"tags\".\"name\" LIKE 'ruby' ESCAPE '!')))"
```
Note, how the SQL query is `articles.*, articles.column_a`
After:
[25] pry(main)> Article.tagged_with([:ruby], any: true).unscope(:select).select(:id, :name).to_sql
=> "SELECT \"articles\".\"id\", \"name\" FROM \"articles\" WHERE EXISTS (SELECT * FROM \"taggings\" WHERE \"taggings\".\"taggable_id\" = \"articles\".\"id\" AND \"taggings\".\"taggable_type\" = 'Article' AND \"taggings\".\"tag_id\" IN (SELECT \"tags\".\"id\" FROM \"tags\" WHERE (\"tags\".\"name\" LIKE 'ruby' ESCAPE '!')))"
```
`articles.*` is gone :-)
- https://github.com/mbleigh/acts-as-taggable-on/issues/936
- 47da5036de/lib/acts_as_taggable_on/taggable/tagged_with_query/any_tags_query.rb (L2-L8)
* Add dependent clauses to Article model
* Comment should be valid without a commentable
* Add cleanup scripts
* Cache the comments ids before destroying the article
* Add dependent clause to Article.user_subscriptions as well
* Fix specs
* Enable new Rails/* cops and use autocorrect on them
* Fixed Rails/PluckInWhere leftovers
* Fix Rails/DefaultScope
* Enable and fix Rails/PluckId
* Fix manual mistake with forcing autocorrection on Rails/PluckId
* Apply PR feedback to remove Rails/PluckId inline disables
* Apply PR feedback to get rid of Rails/PluckInWhere inline