1
\$\begingroup\$

Given a PPCG user's user ID, calculate how much times is their Each SE site reputation is from average reputation in the Stack exchange sites they have made accounts.

Let me explain, If someone's total network reputation (Total reputation in the stack exchange sites he has participated) is 10k, and if he has participated in 4 sites, then the average network wide reputation will be 2500

And assume he/she participated in these 4 sites and has these reputation

Stack overflow 3000
Code golf and Coding challenges 2000
Mathematics 1500
Super User 4000

So your final output should be:

Stack overflow 1.2x
Code golf and coding challenges 0.8x
Mathematics 0.6x
Super User 1.8x

Here the x is to express how much times each site reputation is of the average, such as 2500*1.2=3000

  • (No obligation to floor the 1.2)
  • (Order of the sites does not matter)
  • (You can write the site names uppercase or lowercase or anything, that does not also matter)

Your code will be able to access internet freely.

Input is the PPCG User ID.

Trailing/leading whitespace allowed in output, Standard loopholes apply, this is shortest code wins.

Test case

user 95516 (@rak1507, chosen him/her because have participated in less sites)

For average 943:

Code golf and coding challenges 3.57x
Stack overflow 0.21x
Meta stack exchange 0.107x
Puzzling 0.107x
\$\endgroup\$
12
  • \$\begingroup\$ I don't think "any kind of hardcoded output is not allowed" is necessary - it's unlikely someone's going to download the reputations of every user on SE :) \$\endgroup\$ – user Mar 22 at 13:49
  • 1
    \$\begingroup\$ I'm fairly familiar with the SEAPI, and I'm not sure it's possible to get someone's user info without the user id, and your ID is different on each site \$\endgroup\$ – caird coinheringaahing Mar 22 at 14:02
  • 1
    \$\begingroup\$ @Wasif A better input to provide would be a network user ID as a full list of a user's reputation on different sites is shown there \$\endgroup\$ – caird coinheringaahing Mar 22 at 14:06
  • 10
    \$\begingroup\$ This is why the sandbox is useful \$\endgroup\$ – rak1507 Mar 22 at 14:08
  • 3
    \$\begingroup\$ @Wasif all challenges should be posted to the sandbox, just to avoid any errors that weren't spotted. There's really no reason not to \$\endgroup\$ – rak1507 Mar 22 at 17:23
4
\$\begingroup\$

JavaScript (Node.js), 465 bytes

(i,C)=>(p='https://codegolf.stackexchange.com',n=t=a=[],g=(u,c)=>require('https').get(u,r=>r.on('data',d=>i+=d,r.on('end',_=>c(r.headers.location)))))(p+'/users/'+i,l=>g(p+l,_=>g(s=i.match(/ht.+\/s[^"]+\d\/[^?]/)+0,l=>g(new URL(l,s)+'?tab=accounts',_=>i.replace(/unt-[^]+?\n *([\w&].+)[^]+?r".+?([\d,]+)/g,(_,s,r)=>a.push([s,r=r.split`,`.join``,t-=r,n--]))&&C(a.map(([s,r])=>s.replace(/&#?(.+?);/g,(_,m)=>String.fromCharCode('0'+m|0||38))+` ${r/t*n}x`).join`
`)))))

Function which accepts a the ID as a string and returns the result to a callback.

I tried to make it robust and it should work for any profile as of time of submission with one possible exception: the accounts tab on the network profile contains a profile summary which can be edited by the user so it's possible for a user to write something to fool the regex in their bio.

Explanation

(i,C)=>(p='https://codegolf.stackexchange.com',
// n = count of sites, t = total sum of reputation, a = array of (siteName, rep) tuples
n=t=a=[],
// Function which performs a GET request.
// The body is appended to `i` and the callback is called with the location header.
g=(u,c)=>require('https').get(u,r=>r.on('data',d=>i+=d,r.on('end',_=>c(r.headers.location))))
// Load the user's PPCG profile.
)(p+'/users/'+i,l=>
// Since we only provided the ID and not the name in the URL, it will return a 301 redirect.
// Load the redirect.
g(p+l,_=>
// The first URL on the body to match this regex is the network profile URL of the user.
// Note that this comes before the custom content in the profile
// (which could contain other network profile URLs).
// This request will result in a 301 if the user's name is different on the
// network profile than PPCG, so the first character plus 0 will force a redirect
// because display names are a minimum of 3 characters long.
g(s=i.match(/ht.+\/s[^"]+\d\/[^?]/)+0,l=>
// Load the accounts tab of the network profile.
g(new URL(l,s)+'?tab=accounts',_=>
// Find each site and reputation count.
i.replace(/unt-[^]+?\n *([\w&].+)[^]+?r".+?([\d,]+)/g,
// Remove the commas from the rep value, populate `a` and add them up for the average.
// decodeURIComponent is annoyingly long but there's no shorter way to correctly display
// sites like the Japanese site which is made solely of URI encoded characters.
(_,s,r)=>a.push([s,r=r.split`,`.join``,t-=r,n--]))&&
// Calculate and print the results.
// There is a lot of bytes dedicated here to unescaping HTML entities because
// unfortunately there's no easy way to correctly display sites like the Japanese site
// which is made solely of HTML encoded characters.
// As far as I can tell the only escapes used in site names are the `&#xXX` style
// and `&`.
C(a.map(([s,r])=>s.replace(/&#?(.+?);/g,(_,m)=>String.fromCharCode(0+m|0||38))+` ${r/t*n}x`).join`
`)))))
\$\endgroup\$
1
  • \$\begingroup\$ Just be careful to make sure this works if a user hides communities from their community list. For example, I've hidden Area51 (where I have 208 rep) and StackOverflow (where I have ~1.1k rep) - userid 78850. \$\endgroup\$ – lyxal Mar 23 at 3:55
2
\$\begingroup\$

Python 3 + Selenium + Firefox, 301 bytes

from selenium.webdriver import*
D=Firefox()
D.get("http://codegolf.stackexchange.com/users/"+input())
F=D.find_elements_by_css_selector
F('.communities a')[-1].click()
s=[int(j.text[:~9].replace(',',''))for j in F('.account-stat')[::4]]
for i,j in zip(F('h2'),s):print(i.text,str(j*len(s)/sum(s))+'x')

This program did not close the Firefox window after execute, you need to close it manually. Input from stdin, output to stdout. You should completely ignore the Firefox window.

from selenium.webdriver import*
# start Firefox WebDriver
D=Firefox()
# Ask input for user ID
# Open profile page on CodeGolf.SE
D.get("http://codegolf.stackexchange.com/users/"+input())
# Store find_elements_by_css_selector to save bytes
F=D.find_elements_by_css_selector
# `.communities` is the left pannel of user's communities
# the last link (`a`) in it is accounts tab of user profile on SE
# click to follow the link
F('.communities a')[-1].click()
# `.account-stat` selects all user stat cells
# 4 cells each row, 1st cell each row is reputation, so we [::4]
# the cell contains text like `12,345 reputation`
# We only keep digits and convert to int
s=[int(j.text[:~9].replace(',',''))for j in F('.account-stat')[::4]]
# `h2` selects all nodes for sites name
# for each sites name and its reputation, output as required
for i,j in zip(F('h2'),s):print(i.text,str(j*len(s)/sum(s))+'x')

Not much rooms for golfing here. Only golfed things would be F=D.find_elements_by_css_selector and [:~9], IMO.

\$\endgroup\$
3
  • \$\begingroup\$ Just be careful to make sure this works if a user hides communities from their community list. For example, I've hidden Area51 (where I have 208 rep) and StackOverflow (where I have ~1.1k rep) - userid 78850 \$\endgroup\$ – lyxal Mar 23 at 3:56
  • \$\begingroup\$ @Lyxal I didn't know one can hide some communities before. This program will not count the hidden communities as I just tried. Is this acceptable behavior? Or is there any ways to get the hidden communities and reputation on it? \$\endgroup\$ – tsh Mar 23 at 4:14
  • \$\begingroup\$ Honestly, I'm not sure. StackExchange still registers my full network rep (~13.4k iirc) on chat. I tried logging out, but there seems to be no way of seeing all communities. You'd have to ask OP if this is acceptable. \$\endgroup\$ – lyxal Mar 23 at 4:16
2
\$\begingroup\$

PowerShell, 331 bytes

There are some issues with this method, but ones that I'm not sure can be resolved without reworking the problem to use the API rather than web scraping - hidden sites will not be counted via this method, and the user editable portion of the profile page could potentially contain something which fools my Regex.

Definitely room for golfing improvement here, especially in the regex; I will revisit this when I have the time. Here are the user IDs I used for validating: 92116, 99902, 66833, 95516

$x=(iwr "codegolf.stackexchange.com/users/$args")-match"https://\w+\.\w+/\w+/\d+/[^""]*(?="")"
$a=(iwr($Matches[0]+"?tab=accounts")|Select-String "2>\s*<a[^>]+>([^<]+)<(.|\n)*?>([0-9,]+)<"-AllMatches).Matches|%{@{n=$_.Groups[1].Value.Trim();r=$_.Groups[3].Value-replace","}}
$a|?{$r+=[Int]$_.r}
$a|%{$_.n+" $($_.r/($r/$a.Count))x"}

No TIO, because internet :(

Example

Input:

66833      # This is user ChartZ Belatedly, who recommended their ID as a test case.

Output:

Code Golf 24.5032822757112x
Movies &amp; TV 2.72347840181538x
Meta Stack Exchange 1.07869357322311x
Stack Overflow 0.615122781424751x
Science Fiction &amp; Fantasy 0.571440149120674x
Code Review 0.343220682389173x
Interpersonal Skills 0.323608071966934x
Mathematics 0.314693249047735x
Area 51 0.212172785476943x
MathOverflow 0.161358294837507x
English Language &amp; Usage 0.12748196774455x
German Language 0.116784180241511x
Stack Apps 0.0989545344031121x
Computer Science Educators 0.0989545344031121x
Role-playing Games 0.0900397114839128x
Academia 0.0900397114839128x
Mathematica 0.0900397114839128x
Mi Yodeya 0.0900397114839128x
Philosophy 0.0900397114839128x
Arqade 0.0900397114839128x
The Workplace 0.0900397114839128x
Database Administrators 0.0900397114839128x
Puzzling 0.0900397114839128x
Super User 0.0900397114839128x
Physics 0.0900397114839128x
Chemistry 0.0900397114839128x
Personal Finance &amp; Money 0.0900397114839128x
Quantum Computing 0.0900397114839128x
Coffee 0.0900397114839128x
3D Printing 0.0900397114839128x
Medical Sciences 0.0900397114839128x
Economics 0.0900397114839128x
Politics 0.0900397114839128x

Bonus, PowerShell, 347 bytes

This version uses the same metric as SE, in which sites where the user has less than 200 rep are not counted.

$x=(iwr "codegolf.stackexchange.com/users/$args")-match"https://\w+\.\w+/\w+/\d+/[^""]*(?="")"
$a=(iwr($Matches[0]+"?tab=accounts")|Select-String "2>\s*<a[^>]+>([^<]+)<(.|\n)*?>([0-9,]+)<"-AllMatches).Matches|%{@{n=$_.Groups[1].Value.Trim();r=[Int]($_.Groups[3].Value-replace",")}}|?{$_.r-ge200}
$a|?{$r+=$_.r}
$a|%{$_.n+" $($_.r/($r/$a.Count))x"}

Example

Input:

66833      # This is user ChartZ Belatedly, who recommended their ID as a test case.

Output:

Code Golf 7.18671741088289x
Movies &amp; TV 0.798785625054473x
Meta Stack Exchange 0.316376630545307x
Stack Overflow 0.180413119897737x
Science Fiction &amp; Fantasy 0.1676011737021x
Code Review 0.100665291537143x
Interpersonal Skills 0.094912989163592x
Mathematics 0.0922983062665233x
Area 51 0.0622294529502339x
\$\endgroup\$
4
  • \$\begingroup\$ This is such a broken "metric" for site wide rep, simply because of the "all sites count" bit. As a comparison, my CGCC rep is "actually" 7.2 times my average rep/site, not 24.5 times :) \$\endgroup\$ – caird coinheringaahing Mar 22 at 17:53
  • \$\begingroup\$ @ChartZBelatedly it makes me wonder if SE ever used a calculation metric like this, and then realized how skewed it was before changing it to the current (far superior) method. Definitely agreed that this metric is broken - It certainly wouldn't fly in my day job! \$\endgroup\$ – Zaelin Goodman Mar 22 at 17:58
  • \$\begingroup\$ This doesn't work if a user hides communities from their community list. I've hidden Area51 (where I have 208 rep) and StackOverflow (where I have ~1.1k rep). \$\endgroup\$ – lyxal Mar 23 at 3:55
  • \$\begingroup\$ @Lyxal thanks! I've added a bit about that at the beginning of my answer - I don't see a way around that limitation so, for now, I will leave it at that. \$\endgroup\$ – Zaelin Goodman Mar 23 at 12:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.