15

Answers to Who asked the most number of questions on Stack Exchange? include SEDE scripts for site-specific and network-wide methods to rank users who've asked the most questions. Someone pointed me to it recently and noted that I'm pretty high up there.

Of course this is Stack Exchange and what we're really after is (among other things) generation of good answers1. So I'd like to ask:

Question: How many answers did I facilitate and (roughly) how much answer rep was generated?

I'm primarily interested in a single site at a time query. Of course by "I" I mean whomever runs the script can see their own results.

"bonus points" for a feature that shows the top rep recipients for those answers.


1@uhoh's lemmas:

  • @uhoh's lemma #1: Stack Exchange is a collaborative effort to generate good answers to on-topic questions.
  • @uhoh's lemma #2: A Stack Exchange answers can not exist without someone taking the time and showing the effort and interest to post the question.
  • @uhoh's lemma #3: Stack Exchange is both a floor wax and a desert topping2. Asking of questions is a superposition of (at least) two things; seeking solutions to our immediate problem or query and the facilitation of interesting, helpful and informative answers for the benefit of future readers.

2Shimmer, by the Not Ready For Prime Time Players

1

2 Answers 2

11

Here is a query to see how many answers you received on a site, what their total score is and how that breaks down per author.

enter image description here

Reputation is a different matter. You have to account for the reputation cap, and since vote times are not stored, only vote dates, there's no way to see which upvotes should not be counted: those on an answer to your question, or another post. For a rough estimate, you can multiply the total score by 10.

0
3

The following is a similar query to the one in Glorfindel's answer, but implemented as a runnable snippet and using the Stack Exchange API.

The generated report includes the following stats:

  • link to the answering user profile
  • total number of answers by the user
  • total number of community wiki answers
  • total number of accepted answers
  • total reputation earned via bounties
  • total score of answers
  • rough total reputation earned estimate

Example report for a user id 953887 on Stack Overflow:

sample report for a user id 953887 on Stack Overflow

/// <reference types="@userscripters/stackexchange-api-types" />

const API_BASE = "https://api.stackexchange.com";
const API_VER = 2.3;
const API_KEY = "46zkb16JUl3s3jZdIsJDVA((";

/**
 * @summary delays script execution
 * @param {number} [ms] milliseconds to delay for
 * @returns {Promise<void>}
 */
const delay = (ms = 100) => new Promise((resolve) => setTimeout(resolve, ms));

/**
 * @summary makes a table cell
 * @param {string| Node} content
 * @returns {HTMLTableCellElement}
 */
const makeCell = (content) => {
    const td = document.createElement("td");
    td.append(content);
    return td;
};

/**
 * @summary makes a table row
 * @param {Array<string | Node>} columns
 * @returns {HTMLTableRowElement}
 */
const makeRow = (...columns) => {
    const tr = document.createElement("tr");
    const cells = columns.map(makeCell);
    tr.append(...cells);
    return tr;
};

const makeLink = (url, label) => {
    const a = document.createElement("a");
    a.target = "_blank";
    a.href = url;
    a.innerText = label;
    return a;
};

/**
 * @see https://api.stackexchange.com/docs/questions-on-users
 *
 * @summary gets question info by user id
 * @param {number} id user id
 * @param {{ site?: string, page?: number, ...rest }} [options] request configuration
 * @returns {Promise<StackExchangeAPI.Question[]>}
 */
const getQuestions = async (id, { site = "stackoverflow", page = 1, ...rest }) => {
    const url = new URL(`${API_BASE}/${API_VER}/users/${id}/questions`);
    url.search = new URLSearchParams({
        site,
        page: page.toString(),
        ...rest,
    }).toString();

    const res = await fetch(url.toString());
    if (!res.ok) return [];

    const { items = [], has_more = false, backoff } = await res.json();

    if (backoff) {
        await delay(backoff * 1e3);
        return getQuestions(id, { site, page, ...rest });
    }

    if (has_more) {
        items.push(
            ...(await getQuestions(id, {
                site,
                page: page + 1,
                ...rest,
            }))
        );
    }

    return items;
};

/**
 * @see https://api.stackexchange.com/docs/answers-on-questions
 *
 * @summary gets answers by question ids
 * @param {number[]} ids user id
 * @param {{ site?: string, page?: number, ...rest }} [options] request configuration
 * @returns {Promise<StackExchangeAPI.Answer[]>}
 */
const getAnswers = async (ids, { site = "stackoverflow", page = 1, ...rest }) => {
    const url = new URL(`${API_BASE}/${API_VER}/questions/${ids.join(";")}/answers`);
    url.search = new URLSearchParams({
        site,
        page: page.toString(),
        ...rest,
    }).toString();

    const res = await fetch(url.toString());
    if (!res.ok) return [];

    const { items = [], has_more = false, backoff } = await res.json();

    if (backoff) {
        await delay(backoff * 1e3);
        return getAnswers(ids, { site, page, ...rest });
    }

    if (has_more) {
        items.push(
            ...(await getAnswers(ids, {
                site,
                page: page + 1,
                ...rest,
            }))
        );
    }

    return items;
};

/**
 * @template {unknown} T
 *
 * @summary splits an array into chunks
 * @param {T[]} arr array to split
 * @param {number} size chunk size
 * @returns {T[][]}
 */
const chunkify = (arr, size) => {
    /** @type {T[][]} */
    const output = [];

    arr.forEach((e, i) => {
        let chunk = output[output.length - 1];
        i % size ? chunk.push(e) : output.push([e]);
    });

    return output;
};

/**
 * @template {object} T
 *
 * @summary get a sum of items by key
 * @param {T[]} items items to process
 * @param {keyof T} key key to use for the ssum
 * @returns {number}
 */
const getSum = (items, key) => items.reduce((a, c) => a + (c[key] || 0), 0);

/**
/**
 * @template {object} T
 *
 * @summary get a total of items by key
 * @param {T[]} items items to process
 * @param {keyof T} key key to use for the total
 * @returns {number}
 */
const getTotal = (items, key) => items.reduce((a, c) => a + (c[key] ? 1 : 0), 0);

/**
 * @summary sums score of all posts
 * @param {Array<StackExchangeAPI.Post>} posts posts to process
 * @returns {number}
 */
const getTotalScore = (posts) => getSum(posts, "score");

window.addEventListener("load", () => {
    const accountInput = document.getElementById("account_id");
    const submit = document.getElementById("search");
    const siteInput = document.getElementById("site");
    const progress = document.getElementById("status");
    progress.value = 0;

    submit.addEventListener("click", async () => {
        const { value: site } = siteInput;
        const { value: userId } = accountInput;

        const report = document.querySelector("#preview > tbody");

        const questions = await getQuestions(userId, { key: API_KEY, site });

        progress.value = 25;

        const questionIds = questions.map(({ question_id }) => question_id);

        const chunks = chunkify(questionIds, 100); // max number of ids is 100
        const { length: numChunks } = chunks;

        const progressValuePerChunk = 75 / numChunks;

        /** @type {Record<string, { answers: StackExchangeAPI.Answer[], link: string, name: string }>} */
        const answers = {};
        for (const chunk of chunks) {
            const items = await getAnswers(chunk, { key: API_KEY, site, filter: "!nKzQUREjAc" });
            items.forEach((answer) => {
                const { owner: { user_id, display_name, link } } = answer;
                if (!answers[user_id]) answers[user_id] = { answers: [] };
                const userAnswers = answers[user_id];
                userAnswers.answers.push(answer);
                userAnswers.link = link;
                userAnswers.name = display_name;
            });

            progress.value += progressValuePerChunk;
        }

        report.querySelectorAll("tr").forEach((r) => r.remove());

        const answersByUser = Object.values(answers);

        answersByUser.sort((a, b) => getTotalScore(b.answers) - getTotalScore(a.answers));

        const upvoteRep = 10;
        const acceptRep = 15;

        const rows = answersByUser.flatMap(({ link, name, answers }) => {
            const userLink = makeLink(link, name);
            const totalAnswers = answers.length;
            const totalAccepted = getTotal(answers, "is_accepted");
            const totalWikis = getTotal(answers.filter((a) => a.community_owned_date), "community_owned_date");
            const totalBounties = getSum(answers, "awarded_bounty_amount");
            const totalScore = getTotalScore(answers);
            const totalNonWikiScore = getTotalScore(answers.filter((a) => !a.community_owned_date));
            const repEstimate = totalNonWikiScore * upvoteRep + totalAccepted * acceptRep + totalBounties;

            return makeRow(
                userLink,
                totalAnswers,
                totalWikis,
                totalAccepted,
                totalBounties,
                totalScore,
                repEstimate
            );
        });

        report.append(...rows);

        report.closest("table").classList.remove("hidden");

        progress.value = 100;
    });
});
label {
  display: block;
  font-weight: 600;
}

caption,
label {
  font-weight: 600;
  margin-bottom: 0.5vh;
}

table {
  margin-top: 4vh;
  border-collapse: collapse;
}

th,
td {
  padding: 2vh 2vw;
  border: 1px solid lightgray;
}

.hidden {
  display: none;
}

progress {
  margin-top: 2vh;
  height: 20px;
  border-radius: 0;
}
<form>  
  <label for="site">Site API Slug</label>
  <input id="site" type="text" title="Site API slug" placeholder="stackoverflow" value="stackoverflow" />

  <label for="account_id">User Account Id</label>
  <input id="account_id" type="text" title="Account Id" placeholder="12345" />
  <button id="search" type="button">Get Info</button>
</form>

<progress id="status" max="100" value="0"></progress>

<table id="preview" class="hidden" cellspacing="0">
  <caption>Stack Exchange Post Activity</caption>
  <thead>
    <tr>
      <th>Account</th>
      <th>Answers</th>
      <th>Wikis</th>
      <th>Accepted</th>
      <th>Bounty rep</th>
      <th>Score</th>
      <th>Rep Estimate</th>
    </tr>
  </thead>
  <tbody>
  </tbody>
</table>

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .