Badges based on User Custom Fields?

Has anyone come up with any badges based on some value for custom user fields? If so, how can we get at those custom user fields with a SQL query?


Here’s a sample badge (there is surely better SQL to do this!) that we quickly hacked together to look for people who filled out an optional custom user field, in this example, to list their GitHub User ID:

    SELECT cf.user_id user_id, cf.updated_at granted_at
    FROM user_custom_fields cf
    WHERE cf.name like 'user_field_2' AND
    LENGTH(cf.value) > 1

Note that the value of name field is actually user_field_1, user_field_2, etc., and not the name that you’ve assigned in the admin section.

16 Likes

Awesome badge. Thank you for sharing this!

I :heartpulse: this - thanks for sharing! Works for me.

One thing I noticed: it’s not immediately apparent on the custom user field which user_field_n goes which which user field and it doesn’t start at 1! I found it by looking at the source of that page.

Do you know how to modify this query to display it only when a user has another badge already? I’d like to display a “Tech Ambassador” badge on my site to people but only to those users who have self-identified themselves as such and have also provided a bio.

3 Likes

I think you’d simply add the following:

AND EXISTS (
  SELECT 1 FROM user_badges ib
  WHERE cf.user_id = ib.user_id
  AND ib.badge_id = 888
)

Where 888 is the badge ID number of the other prerequisite badge. See the following for details:

6 Likes

Well the name given should be in a table somewhere, though joining on it to create a dynamically generated field name may not make the query more self documenting (if that’s even possible in postgres)…

And you’ll have to be careful when editing (specifically adding and removing other) fields of course.

1 Like

Agreed. I’m hopeful to find an answer related to that here:

I had an issue with setting that to work on user edit or creation so I had to modify it a bit. If someone wants to do the same, you need to use the following code:

SELECT cf.user_id user_id, cf.updated_at granted_at
FROM user_custom_fields cf
WHERE cf.name = 'user_field_2'
AND (
  cf.user_id IN (:user_ids)
  OR :backfill
)
AND length(cf.value) > 1
3 Likes

Is there a way to modify the above code so that the badge links to the post which granted the badge, similar to how “First Share” works?

Also, if I set that badge to be allowed to be given more than once, will the above code do it for multiple posts in the same thread? If not, how could I do that?

Thanks in advance :smile:

There is no post associated with filling out the custom fields, unless I’m missing something from your query.

But in general, to associate posts you need to tick the Query targets posts checkbox and include a posts.id (or post_id if it’s a foreign key) aliased to post_id and if you use the When a user edits or creates a post trigger you’ll need to include :backfill and :post_ids in the query (see this post futher up for an example)

Gah, you’re right, sorry I referred the wrong code.

I got the following

SELECT
DISTINCT ON (p.user_id)
p.user_id, p.id post_id, p.created_at granted_at
FROM badge_posts p
WHERE p.topic_id = 110 AND
  (:backfill OR p.id IN (:post_ids) )

which I took from another tip in the thread. This automatically grants a user who posts in a specific post a badge. Would ticking “Query Target posts” link that to the badge?

Yes, that query should be marked as targets posts, show post on the public badges page, and trigger on post creation.

Thanks. Can you tell if the same query also grant the badge multiple times if the appropriate checkbox is ticket?

Yes, if that box is checked then multiple badges would be awarded.

May I ask why you have the DISTINCT ON (p.user_id) clause in there? If you’re after multiple badges that runs the risk of some of them not being assigned.

2 Likes

I merely copied it from a post in here for assigning badges for on posting on a topic. It was this part which made me wonder if it can assign multiple badges. Do you think I can safely remove it?

EDIT: Just removed it and it seems to work fine. Thanks

Yes. It’s the difference between (for example):

[pjh@sofa ~]$ psql -d discourse -c "SELECT
> DISTINCT ON (p.user_id)
> p.user_id, p.id post_id, p.created_at granted_at
> FROM badge_posts p
> WHERE p.topic_id = 1000
> LIMIT 20"
 user_id | post_id |         granted_at         
---------+---------+----------------------------
      -1 |   34753 | 2014-07-19 17:21:48.438813
       4 |   52265 | 2014-08-08 01:16:23.26294
       6 |  186028 | 2015-01-02 18:05:22.61456
      15 |   63069 | 2014-08-21 20:21:46.150202
      16 |   31387 | 2014-07-16 12:51:37.874941
      17 |   80603 | 2014-09-13 00:25:50.532543
      18 |  120362 | 2014-10-15 13:47:01.920886
      20 |   12967 | 2014-06-19 15:49:18.492014
      28 |   13208 | 2014-06-19 20:36:59.371834
      30 |   35624 | 2014-07-21 17:28:57.3986
      33 |   89920 | 2014-09-21 14:20:44.817275
      43 |   13300 | 2014-06-19 21:18:48.065129
      50 |  143837 | 2014-11-06 05:17:54.776199
      60 |  154883 | 2014-11-18 21:49:56.792488
      69 |  428292 | 2015-05-22 17:21:00.939645
      86 |   14841 | 2014-06-20 22:46:50.493649
     110 |  120917 | 2014-10-15 19:32:08.440928
     123 |   51260 | 2014-08-07 03:31:05.654638
     183 |  160245 | 2014-11-25 21:12:52.132026
     261 |  187712 | 2015-01-05 21:24:05.746039
(20 rows)
[pjh@sofa ~]$ psql -d discourse -c "SELECT
p.user_id, p.id post_id, p.created_at granted_at
FROM badge_posts p
WHERE p.topic_id = 1000
LIMIT 20"
 user_id | post_id |         granted_at         
---------+---------+----------------------------
     589 |  195565 | 2015-01-13 16:11:05.705177
     294 |  430986 | 2015-05-25 17:01:07.148368
     606 |  430988 | 2015-05-25 17:01:13.176588
     606 |  430990 | 2015-05-25 17:01:40.033308
     762 |   37144 | 2014-07-22 19:04:02.591978
     579 |  430599 | 2015-05-25 14:33:06.928209
     294 |  430989 | 2015-05-25 17:01:32.901402
     922 |  430109 | 2015-05-25 00:28:35.562221
     922 |  430027 | 2015-05-24 21:57:25.817651
     606 |  430111 | 2015-05-25 00:29:11.922553
     606 |  430026 | 2015-05-24 21:56:50.236247
     579 |  430025 | 2015-05-24 21:56:46.181157
     922 |  418379 | 2015-05-14 07:41:03.747217
     123 |  209061 | 2015-01-26 03:49:34.27203
     294 |  430991 | 2015-05-25 17:02:00.277703
     762 |  125242 | 2014-10-19 16:24:21.902349
     606 |  145216 | 2014-11-07 14:42:28.916019
     922 |  431105 | 2015-05-25 20:30:49.410084
     294 |  431106 | 2015-05-25 20:31:18.747891
     294 |  431103 | 2015-05-25 20:30:45.923232
(20 rows)

[pjh@sofa ~]$ 

Note the repetition of user 922 in the second query - that’s what’s required to give more than one badge.

The first appears to pick (semi) random post numbers to apply badges to

3 Likes

If you want your query to not be dependent on system field labels such as user_field_2, you can do the following:

    SELECT cf.user_id user_id, cf.updated_at granted_at
    FROM user_custom_fields cf
    INNER JOIN user_fields uf
      ON uf.id = (0 || regexp_replace(cf.name, 'user_field_', ''))::integer
      AND uf.name = 'FriendlyFieldName'
    WHERE LENGTH(cf.value) > 0

It seems like you’d want to check for a length value greater than zero, just in case you have some single-digit UID GitHub graybeards. And if you are granting a badge multiple times, you’ll want to add GROUP BY cf.user_id to be safe.

We have a dev, a staging, and a live site, and the custom user fields we manually add don’t always have the same user_field_n values from site to site.

5 Likes

I’m wondering which page you were referring to (assuming this info is still up to date)? I’m attempting to find user_field_n, but examining page source for Admin > Customize > User Fields, as well as in user profiles, turns up nothing.

1 Like

I’m attempting to use this code, and I’m getting the following response: ERROR: invalid input syntax for type integer: "0show_quick_messages", referring to the user settings for the Quick Messages plugin, I’m guessing (even though it’s now disabled).

I’d be grateful for any suggestions about how to avoid or bypass this error.

1 Like

You’re right! I am not immediately finding the custom user field id now myself. You might try with a data explorer query.

1 Like

Thanks—I’m not familiar with those, but I’ll look it up here.

I also found @OnceWas’s post that would allow for looking up the custom user field by name, but ran into an error that I don’t know how to circumvent.

1 Like