Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modifying the data type being used for allChallenges in api_processor… #401

Merged
merged 4 commits into from
Jul 31, 2023
Merged

Modifying the data type being used for allChallenges in api_processor… #401

merged 4 commits into from
Jul 31, 2023

Conversation

ngillux
Copy link
Contributor

@ngillux ngillux commented Jul 27, 2023

… from array to object.

Checklist:

Closes #400

@GuillermoFloresV
Copy link
Member

If possible, could we have screenshots that show the difference between the old and new approach? Possibly screenshots of the data and what is being processed. Also, is this a fix or possible fix? just so we have an idea on where we are in fixing the problem.

@lloydchang
Copy link
Contributor

@ngillux @GuillermoFloresV

FWIW, I asked OpenAI ChatGPT:

What are the differences between these two lines of JavaScript code?
]['challengeOrder'].map(x => x[0]),
]['challengeOrder'].map(x => Object.values(x)),

ChatGPT replied to me:

The two lines of JavaScript code appear to be very similar, but there is a subtle difference in their mapping functions:

]['challengeOrder'].map(x => x[0]): In this line, ]['challengeOrder'] accesses the property "challengeOrder" of an array (or an object with numerical properties). The map function is then applied to the "challengeOrder" array, and it extracts the first element (index 0) from each element of the array.
]['challengeOrder'].map(x => Object.values(x)): Similar to the first line, ]['challengeOrder'] accesses the property "challengeOrder" of an array (or an object with numerical properties). The map function is then applied, but instead of directly extracting the first element, it extracts all the values of the elements within the "challengeOrder" array using Object.values(x).

So, in summary:
The first line extracts the first element from each element in the "challengeOrder" array.
The second line extracts all the values of the elements in the "challengeOrder" array and returns them as arrays themselves.

@ngillux
Copy link
Contributor Author

ngillux commented Jul 28, 2023

Hi @lloydchang thanks for your above comment.

Me and @GuillermoFloresV are aware of the difference, we're still figuring out why it was needed / why it was working before and not now.

@lloydchang
Copy link
Contributor

@ngillux @GuillermoFloresV @theGaryLarson

Circling back to 25c41fb

I asked ChatGPT to diff the before and after from @theGaryLarson's change (detailed below):


ChatGPT replied:

The two code snippets you provided are almost identical. It seems like there might be some duplication in the code as both snippets share the same variable names and processing logic. The only difference between the two snippets is how props.studentData is processed:

First Snippet:

let rawStudentSummary = props.studentData.map(studentJSON => {
  // ... data processing logic using props.studentData as an array ...
});

const data = React.useMemo(
  () => mapData(rawStudentSummary),
  [rawStudentSummary]
);

Second Snippet:

let rawStudentSummary = Object.entries(props.studentData).map(([i]) => {
  // ... data processing logic using props.studentData as an object ...
});

const data = mapData(rawStudentSummary);

In the first snippet, props.studentData is expected to be an array of objects, and the map method is used to process each student's data. In the second snippet, props.studentData is expected to be an object with student data stored as key-value pairs, and Object.entries is used to convert the object into an array of key-value pairs before applying the map method for data processing.

Since the data processing logic is the same in both snippets, it seems that the second snippet is a more correct representation, assuming that the props.studentData is an object.

If there are other differences or specific issues you would like to clarify, please provide more context or details so that I can address them accordingly.


I prompted ChatGPT to diff the before and after from @theGaryLarson's change:

what are the differences between:
  let allCertifications = props.certifications.map(col_course => {
    col_course.selector = row => row[`${col_course.dashedName}`];
    return col_course;
  });

  let numChallengesPerCertification = allCertifications.map(certification => {
    let totalNumChallenges = 0;
    certification.forEach(block => {
      totalNumChallenges += block.allChallenges.length;
    });
    return totalNumChallenges;
  });

  let grandTotalChallenges = 0;
  numChallengesPerCertification.forEach(numChallenges => {
    grandTotalChallenges += numChallenges;
  });

  let rawStudentSummary = props.studentData.map(studentJSON => {
    let studentName = studentJSON.email;
    let superBlocks = Object.values(studentJSON.certifications);
    let blocks = [];
    let blockData = [];
    let completionTimestamps = [];

    superBlocks.forEach(superBlock =>
      blockData.push(Object.values(superBlock))
    );

    let blockName = '';
    blockData.forEach(b =>
      b[0].blocks.forEach(
        obj => ((blockName = Object.keys(obj)[0]), blocks.push(blockName))
      )
    );

    let getCompleted = blockData.flat();

    getCompleted.map(obj =>
      obj.blocks.map(challenges => {
        Object.values(challenges)[0].completedChallenges.map(item => {
          completionTimestamps.push(item.completedDate);
        });
      })
    );

    let rawStudentActivity = {
      recentCompletions: completionTimestamps
    };

    let studentActivity = getStudentActivity(rawStudentActivity);

    let numCompletions = completionTimestamps.length;

    let percentageCompletion = (
      <div>
        <label>
          {numCompletions}/{grandTotalChallenges}{' '}
        </label>
        <meter
          id='progress'
          min='0'
          max={grandTotalChallenges}
          value={numCompletions}
        ></meter>
      </div>
    );

    let studentSummary = {
      name: studentName,
      activity: studentActivity,
      progress: percentageCompletion,
      detail: (
        <a
          // TODO:
          href={
            `/dashboard/v2/details/${props.classroomId}/` + `${studentName}`
          }
        >
          {' '}
          details{' '}
        </a>
      )
    };

    return studentSummary;
  });

  const mapData = function (original_data) {
    let table_data = original_data.map(student => {
      let mapped_student = {
        col1: student.name,
        col2: student.activity,
        col3: student.progress,
        col4: student.detail
      };
      return mapped_student;
    });
    return table_data;
  };

  const data = React.useMemo(
    () => mapData(rawStudentSummary),
    [rawStudentSummary]
  );

  const columns = React.useMemo(
    () => [
      {
        Header: 'Student Name',
        accessor: 'col1', // accessor is the "key" in the data
        width: '20%'
      },
      {
        Header: 'Activity',
        accessor: 'col2',
        width: '10%'
      },
      {
        Header: 'Progress',
        accessor: 'col3',
        width: '20%'
      },
      {
        Header: 'Details',
        accessor: 'col4',
        width: '50%'
      }
    ],
    []
  );
  let allCertifications = props.certifications.map(col_course => {
    col_course.selector = row => row[`${col_course.dashedName}`];
    return col_course;
  });

  let numChallengesPerCertification = allCertifications.map(certification => {
    let totalNumChallenges = 0;
    certification.forEach(block => {
      totalNumChallenges += block.allChallenges.length;
    });
    return totalNumChallenges;
  });

  let grandTotalChallenges = 0;
  numChallengesPerCertification.forEach(numChallenges => {
    grandTotalChallenges += numChallenges;
  });

  let rawStudentSummary = Object.entries(props.studentData).map(([i]) => {
    let studentName = Object.keys(props.studentData[i])[0];
    let completionTimestamps = [];
    let blocks;
    try {
      blocks = Object.keys(props.studentData[i][studentName]['blocks']);
    } catch (e) {
      blocks = [];
    }

    for (let j = 0; j < blocks.length; j++) {
      let studentCompletions =
        props.studentData[i][studentName]['blocks'][blocks[j]][
          'completedChallenges'
        ];

      studentCompletions.forEach(({ completedDate }) => {
        completionTimestamps.push(completedDate);
      });
    }

    let rawStudentActivity = {
      recentCompletions: completionTimestamps
    };
    let studentActivity = getStudentActivity(rawStudentActivity);

    let numCompletions = completionTimestamps.length;
    let percentageCompletion = (
      <div>
        <label>
          {numCompletions}/{grandTotalChallenges}{' '}
        </label>
        <meter
          id='progress'
          min='0'
          max={grandTotalChallenges}
          value={numCompletions}
        ></meter>
      </div>
    );
    let studentSummary = {
      name: studentName,
      activity: studentActivity,
      progress: percentageCompletion,
      detail: (
        <a
          href={
            `/dashboard/v2/details/${props.classroomId}/` + `${studentName}`
          }
        >
          {' '}
          details{' '}
        </a>
      )
    };
    return studentSummary;
  });

  const mapData = function (original_data) {
    let table_data = original_data.map(student => {
      let mapped_student = {
        col1: student.name,
        col2: student.activity,
        col3: student.progress,
        col4: student.detail
      };
      return mapped_student;
    });
    return table_data;
  };

  const data = mapData(rawStudentSummary);

  const columns = [
    {
      Header: 'Student Name',
      accessor: 'col1', // accessor is the "key" in the data
      width: '20%'
    },
    {
      Header: 'Activity',
      accessor: 'col2',
      width: '10%'
    },
    {
      Header: 'Progress',
      accessor: 'col3',
      width: '20%'
    },
    {
      Header: 'Details',
      accessor: 'col4',
      width: '50%'
    }
  ];

  return { data, columns };
}

@lloydchang
Copy link
Contributor

lloydchang commented Jul 28, 2023

@ngillux @GuillermoFloresV @theGaryLarson

Based on the explanation from ChatGPT:

In the first snippet, props.studentData is expected to be an array of objects, and the map method is used to process each student's data. In the second snippet, props.studentData is expected to be an object with student data stored as key-value pairs, and Object.entries is used to convert the object into an array of key-value pairs before applying the map method for data processing.

As for the question from @ngillux and @GuillermoFloresV :

why it was working before and not now?

My understanding is the difference in object casting.

Previously, it was casted as an array of objects, hence :

x[0]

Now, it's casted as an object, hence:

Object.values(x)

@ngillux
Copy link
Contributor Author

ngillux commented Jul 28, 2023

@lloydchang the response structure of the JSON data changed hence the differences in the two approaches.

@lloydchang
Copy link
Contributor

lloydchang commented Jul 28, 2023

@ngillux Did the response structure change in a different pull request #366 from @theGaryLarson's #353?

If yes, I believe the fundamental issue is what @theGaryLarson summarized yesterday, "things started to break there because of asynchronous changes in pending PRs."

Meanwhile, I submitted an issue request #403 for @GuillermoFloresV to Enable GitHub's Merge Queue for freeCodeCamp classroom GitHub repository to address the fundamental issue of asynchronous changes in pending PRs.

Thank you!

@lloydchang
Copy link
Contributor

Answering my question to @ngillux earlier:

I believe the response structure changed via freeCodeCamp/freeCodeCamp#50769 (review) and freeCodeCamp/freeCodeCamp#50955

Specifics are expanded at #410 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

The Dashboard table is not rendering due to an error happening in server side logic
4 participants