# (Obsolete) Set category tracking level defaults historically

**URL:** https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165
**Category:** Self-Hosting
**Tags:** how-to
**Created:** [November 21, 2016, 12:45am UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165 "2016-11-21T00:45:27Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [November 21, 2016, 12:45am UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/1 "2016-11-21T00:45:27Z")

</div>

> ⚠ Following the release of **Discourse v2.4.0.beta7** (or if you have [this commit](https://github.com/discourse/discourse/commit/b2f682f35eba0cc3f09588356ecf8d1dd824542f)), admins now have the option to set the category tracking levels of all existing users when setting the default category tracking levels in their site settings:
> 
> 1. Add a category to one of the following site settings, then select the green checkbox button.
> 
> 2. A modal window will pop up with the following options:

* * *

> **The old way using the console**
>
> Discourse allows you to specify
> 
> `default categories watching`  
> `default categories tracking`  
> `default categories muted`  
> `default categories watching first post`
> 
> In site settings.
> 
> These setting apply to all **new** accounts created after the setting is added.
> 
> Sometimes, you may decide to make the change historically and apply it to all **old accounts**. At the moment there is no UI to apply this so you have to crack open a rails console.
> 
> If you are a **hosted customer contact us** and we will sort it out.
> 
> If you self-host try the following:
> 
> Say you want to set `default categories watching first post` on all accounts to point to a category.
> 
> 1. make sure you have a backup
> 
> 2. find the category id
> 
> 3. find the desired notification\_level
> 
> 4. run a query to backfill changes

---

<div class="post-metadata">

### Author: ![alehandrof](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alehandrof/32/119526_2.png) [@alehandrof](https://meta.discourse.org/u/alehandrof)
#### Post date: [November 28, 2016, 11:07am UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/2 "2016-11-28T11:07:52Z")

</div>

Thanks for this how-to, Sam!

Two issues:

- There seems to be an issue with Step 1. (Too many quotes?)
- There’s a `;` missing from the end of the `User.exec_sql("…")` command.

Two questions:

- Say I’ve previously made everyone “watch” a category with ID 5. How can I remove that setting? (I actually have two categories I need to alter. In one I want to remove the setting altogether and in the other I want to replace it with “watch first”.)
- How can I apply this to tags instead of categories?

---

<div class="post-metadata">

### Author: ![zogstrip](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zogstrip/32/512781_2.png) [@zogstrip](https://meta.discourse.org/u/zogstrip)
#### Post date: [November 28, 2016, 11:20am UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/3 "2016-11-28T11:20:32Z")

</div>

> [@alehandrof](#):
>
> Two issues:
> 
> - There seems to be an issue with Step 1. (Too many quotes?)
> - There’s a ; missing from the end of the User.exec\_sql(“…”) command.

I’ve updated @sam’s post with the corrections.

---

<div class="post-metadata">

### Author: ![alehandrof](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alehandrof/32/119526_2.png) [@alehandrof](https://meta.discourse.org/u/alehandrof)
#### Post date: [November 28, 2016, 11:23am UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/4 "2016-11-28T11:23:01Z")

</div>

> [@zogstrip](#):
>
> I’ve updated @sam’s post with the corrections.

The one-liner is still missing the `;` at the end.

 ![](https://global.discourse-cdn.com/meta/original/3X/7/e/7e933bb74cb801598e0d8ae120ff16d93c45e383.png)

---

<div class="post-metadata">

### Author: ![zogstrip](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zogstrip/32/512781_2.png) [@zogstrip](https://meta.discourse.org/u/zogstrip)
#### Post date: [November 28, 2016, 11:23am UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/5 "2016-11-28T11:23:44Z")

</div>

I don’t think that’s needed, but I fixed it anyway 😉

---

<div class="post-metadata">

### Author: ![alehandrof](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alehandrof/32/119526_2.png) [@alehandrof](https://meta.discourse.org/u/alehandrof)
#### Post date: [November 29, 2016, 9:06am UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/6 "2016-11-29T09:06:38Z")

</div>

@zogstrip It could be that I don’t know what I’m doing, but it wasn’t working without the semicolon.

Can I get some help with this?:

> [@alehandrof](#):
>
> Say I’ve previously made everyone “watch” a category with ID 5. How can I remove that setting?

I’ve done the obvious thing: running the script to set the notification level to `1`, but that doesn’t work because:

> [@sam](#):
>
> The above query sets it on all users that do not have an explicit default for the category already set, this means you can re-run it if needed.

My understanding of SQL queries is pretty weak and I wasn’t sure how to counteract this.

---

<div class="post-metadata">

### Author: ![LeoMcA](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/leomca/32/87233_2.png) [@LeoMcA](https://meta.discourse.org/u/LeoMcA)
#### Post date: [November 29, 2016, 6:41pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/7 "2016-11-29T18:41:06Z")

</div>

> [@alehandrof](#):
>
> Say I’ve previously made everyone “watch” a category with ID 5. How can I remove that setting?

I don’t think it’s possible, because the `category_users` table doesn’t have `created_at` or `updated_at` columns. If it did, you could delete all the rows which were created/updated at the time you ran the original query.

---

<div class="post-metadata">

### Author: ![alehandrof](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alehandrof/32/119526_2.png) [@alehandrof](https://meta.discourse.org/u/alehandrof)
#### Post date: [December 2, 2016, 12:46pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/8 "2016-12-02T12:46:36Z")

</div>

I’m afraid I don’t understand your answer. That seems like a weird way of approaching it.

Isn’t there a Ruby or MySQL way of doing (pseudo-code):

```plaintext
for all users
  set notification level of category XYZ to regular

```

---

<div class="post-metadata">

### Author: ![Mittineague](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/mittineague/32/114259_2.png) [@Mittineague](https://meta.discourse.org/u/Mittineague)
#### Post date: [December 2, 2016, 10:00pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/9 "2016-12-02T22:00:24Z")

</div>

> [@alehandrof](#):
>
> Isn’t there a Ruby or MySQL way

That would not revert the changes. For example,

Category XYZ - 15% Watching  
Tracking level change  
Category XYZ - 100% Watching  
Tracking level change  
Category XYZ - none Watching

There is no known way t get back to  
Category XYZ - 15% Watching

like could be done somewhat accurately with the Group and Topic users tables that do have updated\_at fields.

But even then, there are no “previous level” fields available.

---

<div class="post-metadata">

### Author: ![alehandrof](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alehandrof/32/119526_2.png) [@alehandrof](https://meta.discourse.org/u/alehandrof)
#### Post date: [December 3, 2016, 11:59am UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/10 "2016-12-03T11:59:13Z")

</div>

I’m afraid I’m not communicating accurately: I don’t want to “revert” a change. I want to stop all users from watching the “Announcements” category. I guess an alternative would be to create a new category, move all the topics into the new category, delete the old “Announcements” category and rename the new category “Announcements”. It just seems like it should be less painful than that.

---

<div class="post-metadata">

### Author: ![Mittineague](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/mittineague/32/114259_2.png) [@Mittineague](https://meta.discourse.org/u/Mittineague)
#### Post date: [December 3, 2016, 7:52pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/11 "2016-12-03T19:52:12Z")

</div>

> [@alehandrof](#):
>
> It just seems like it should be less painful than that.

It is.

If you want to set the tracking level for a category for all members, that is what the first post in this topic details how to do.

_except_

you will want to not use

```plaintext
WHERE cu.user_id IS NULL

```

_but this instead_

```plaintext
WHERE cu.user_id IS NOT NULL

```

and instead of “4” (watching\_first\_post) use “1” (regular)

---

<div class="post-metadata">

### Author: ![alehandrof](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alehandrof/32/119526_2.png) [@alehandrof](https://meta.discourse.org/u/alehandrof)
#### Post date: [December 4, 2016, 1:54pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/12 "2016-12-04T13:54:15Z")

</div>

Thanks for bearing with me @Mittineague, but I can’t get this to work. The category I’m interested in (“Announcements”) has an ID of 10.

So I ran this:

```plaintext
User.exec_sql("INSERT INTO category_users(category_id, user_id, notification_level) SELECT 10, u.id, 1 FROM users u LEFT JOIN category_users cu ON cu.category_id = 10 AND cu.user_id = u.id WHERE cu.user_id IS NOT NULL;")

```

It didn’t remove the “Announcements” category from users’ watched list:

 ![](https://global.discourse-cdn.com/meta/original/3X/5/8/5810faf69d6ab54f5aab9b84f5875e72220326c9.png)

For the record, the console returned this on the first try:

```plaintext
=> #<PG::Result:0x0000000aa41000 status=PGRES_COMMAND_OK ntuples=0 nfields=0 cmd_tuples=358>

```

And this on the second:

```plaintext
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "idx_category_users_u1"
DETAIL: Key (user_id, category_id, notification_level)=(3, 10, 1) already exists.
from /home/vagrant/.rvm/gems/ruby-2.3.1/gems/rack-mini-profiler-0.10.1/lib/patches/db/pg.rb:50:in `exec'

```

Any ideas?

---

<div class="post-metadata">

### Author: ![fefrei](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/fefrei/32/119538_2.png) [@fefrei](https://meta.discourse.org/u/fefrei)
#### Post date: [December 4, 2016, 4:26pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/13 "2016-12-04T16:26:21Z")

</div>

If you promise you have a backup and won’t shoot me no matter what happens, you might want to try [this](https://meta.discourse.org/t/new-category-automatically-as-muted-or-watching-for-all-users/51654/3):

```ruby
User.all.each do |user| CategoryUser.batch_set(user, :regular, [10]) end

```

This should set the tracking level for all users for category `10` to `:regular`, adapt as needed. **It will overwrite any previous settings users have for that category.**

---

<div class="post-metadata">

### Author: ![alehandrof](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alehandrof/32/119526_2.png) [@alehandrof](https://meta.discourse.org/u/alehandrof)
#### Post date: [December 7, 2016, 6:09pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/14 "2016-12-07T18:09:58Z")

</div>

Sorry for taking so long to respond. I had managed to break my local Discourse running some experiments earlier. I set it up again and tried your suggestion, but no joy ☹

Edit: BTW, that is what I want to do:

> [@fefrei](#):
>
> This should set the tracking level for all users for category `10` to `:regular`, adapt as needed. **It will overwrite any previous settings users have for that category.**

---

<div class="post-metadata">

### Author: ![fefrei](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/fefrei/32/119538_2.png) [@fefrei](https://meta.discourse.org/u/fefrei)
#### Post date: [December 7, 2016, 7:16pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/15 "2016-12-07T19:16:44Z")

</div>

Strange, this did work fine for me (although I used it to switch to `:muted`)…

---

<div class="post-metadata">

### Author: ![alehandrof](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alehandrof/32/119526_2.png) [@alehandrof](https://meta.discourse.org/u/alehandrof)
#### Post date: [December 9, 2016, 7:06pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/16 "2016-12-09T19:06:15Z")

</div>

I’ll look again, maybe I missed or messed up something.

---

<div class="post-metadata">

### Author: ![rriemann](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rriemann/32/115238_2.png) [@rriemann](https://meta.discourse.org/u/rriemann)
#### Post date: [February 1, 2017, 11:42pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/17 "2017-02-01T23:42:21Z")

</div>

I would change the default tracking level of a category for existing users like this:

1. start the rails console: `rails c` once you got into the docker with `./launcher enter app`
2. paste your version of the code below.

```ruby
# levels: :watching :watching_first_post
level = :watching
category_slug = "news"
category = Category.find_by_slug(category_slug)
group_name = "trust_level_0" # all users
group = group = Group.find_by_name(group_name)

group.users.each do |user|
  watched_categories = CategoryUser.lookup(user, level).pluck(:category_id)
  CategoryUser.set_notification_level_for_category(user, CategoryUser.notification_levels[level], category.id) unless watched_categories.include?(category.id)
end

```

To change the notification setting for a group inbox, one can use:

```ruby
GroupUser.where(group_id: group.id).update_all(notification_level: GroupUser.notification_levels[level])

```

I wonder if you can also work without an explicit ruby loop in the case of categories.

---

<div class="post-metadata">

### Author: ![alehandrof](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/alehandrof/32/119526_2.png) [@alehandrof](https://meta.discourse.org/u/alehandrof)
#### Post date: [February 6, 2017, 6:41pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/18 "2017-02-06T18:41:43Z")

</div>

Sorry for taking so long to respond:

> [@fefrei](#):
>
> Strange, this did work fine for me (although I used it to switch to :muted)…

Indeed. It works for `:muted`, but not `:regular`.

I tried @rriemann’s code as well, replacing `level = :regular` and my category, but again it doesn’t work.

My hunch is that `:regular` works differently than watching and tracking. Also, now that I’ve set `:mute` for everyone, How do I unset it? 😉

---

<div class="post-metadata">

### Author: ![fefrei](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/fefrei/32/119538_2.png) [@fefrei](https://meta.discourse.org/u/fefrei)
#### Post date: [February 6, 2017, 7:21pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/19 "2017-02-06T19:21:33Z")

</div>

I’d love to know this, too – maybe @sam can help?

---

<div class="post-metadata">

### Author: ![Mittineague](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/mittineague/32/114259_2.png) [@Mittineague](https://meta.discourse.org/u/Mittineague)
#### Post date: [February 6, 2017, 9:40pm UTC](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165/20 "2017-02-06T21:40:13Z")

</div>

I think the difference might be with using “regular”  
I have seen “normal” used in code before.

I was thinking that as long as the integer “1” was used, what descriptor was used would make no difference. Not tested, but I have a feeling that what is wanted here is `:normal`

But maybe not

> <https://github.com/discourse/discourse/blob/main/app/models/category_user.rb#L150-L153>

[Next page](https://meta.discourse.org/t/obsolete-set-category-tracking-level-defaults-historically/53165.md?page=2)
