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

380 statistics for the statistic cards #404

Merged
merged 20 commits into from
Dec 20, 2024

Conversation

DeboraSerra
Copy link
Contributor

Describe your changes

Briefly describe the changes you made and their purpose.

Issue number

Mention the issue number(s) this PR addresses (e.g., #123).

Please ensure all items are checked off before requesting a review:

  • I deployed the code locally.
  • I have performed a self-review of my code.
  • I have included the issue # in the PR.
  • I have labelled the PR correctly.
  • The issue I am working on is assigned to me.
  • I didn't use any hardcoded values (otherwise it will not scale, and will make it difficult to maintain consistency across the application).
  • I made sure font sizes, color choices etc are all referenced from the theme.
  • My PR is granular and targeted to one specific feature.
  • I took a screenshot or a video and attached to this PR if there is a UI change.
Screenshot 2024-12-19 at 13 13 33 Screenshot 2024-12-19 at 13 13 12 Screenshot 2024-12-19 at 13 02 42

@DeboraSerra DeboraSerra requested a review from erenfn December 19, 2024 21:20
@DeboraSerra DeboraSerra linked an issue Dec 19, 2024 that may be closed by this pull request
2 tasks
Copy link
Contributor

coderabbitai bot commented Dec 19, 2024

Walkthrough

This pull request introduces several enhancements related to the statistics tracking feature for guide logs within the application. It includes a new migration for adding an index to the guide_logs table, the implementation of a statistics controller and service, and the addition of routes for statistics retrieval. Furthermore, it updates the frontend components to dynamically fetch and display user statistics, as well as introduces corresponding test suites to ensure functionality across the application.

Changes

File Change Summary
backend/migrations/20241219181037-add-index-guidelog.js Added migration to create an index on showingTime for guide_logs table
backend/src/models/GuideLog.js Reformatted model definition, added indexes
backend/src/controllers/statistics.controller.js New controller for handling statistics retrieval
backend/src/routes/statistics.routes.js Added new route for statistics with JWT authentication
backend/src/service/statistics.service.js Implemented generateStatistics method to compute guide log statistics
backend/src/server.js Registered new statistics routes
frontend/src/scenes/dashboard/Dashboard.jsx Updated Dashboard component to display dynamic statistics
frontend/src/scenes/dashboard/HomePageComponents/StatisticCards/StatisticCards.jsx Enhanced StatisticCard component with new helper functions
frontend/src/services/statisticsService.js Added getStatistics method for API interaction
backend/src/test/mocks/guidelog.mock.js Updated GuideLogBuilder for dynamic guideType handling
backend/src/test/unit/controllers/statistics.test.js Introduced test suite for getStatistics method
backend/src/test/unit/services/statistics.test.js Introduced test suite for generateStatistics method
backend/src/test/e2e/statistics.test.mjs Added end-to-end test suite for statistics API

Possibly Related PRs

Suggested Reviewers

  • gorkem-bwl
  • swoopertr

Tip

CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. We would love to hear your feedback on Discord.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🧹 Nitpick comments (7)
frontend/src/services/statisticsService.js (1)

6-6: That endpoint lookin' kinda sus with that trailing slash! 👀

Consider removing the trailing slash from the API endpoint for consistency.

-    const response = await apiClient.get(`/statistics/`);
+    const response = await apiClient.get('/statistics');
backend/migrations/20241219181037-add-index-guidelog.js (2)

1-2: Yo, that 'use strict' is as redundant as putting ketchup on poutine!

Since we're working with ES modules, they're automatically in strict mode. Let's keep it clean, eh?

-"use strict";
-
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)


6-11: Those example comments are just taking up space like a moose in a phone booth!

The example comments don't add value to our migration. Let's remove them to keep our code as clean as a fresh sheet of ice.

  async up(queryInterface, Sequelize) {
-    /**
-     * Add altering commands here.
-     *
-     * Example:
-     * await queryInterface.createTable('users', { id: Sequelize.INTEGER });
-     */
    await queryInterface.addIndex("guide_logs", ["showingTime"], {

Also applies to: 22-26

frontend/src/scenes/dashboard/HomePageComponents/StatisticCards/StatisticCards.jsx (1)

23-27: This JSX is cleaner than fresh snow, but we can make it even better!

Let's extract the arrow logic into a separate component to keep things tidy.

+ const ChangeIndicator = ({ changeRate }) => {
+   if (changeRate === 0) return null;
+   return changeRate >= 0 ? <ArrowUpwardIcon /> : <ArrowDownwardIcon />;
+ };

  return (
    <div className={styles.statisticCard}>
      <div className={styles.metricName}>{metricName}</div>
      <div className={styles.metricValue}>{metricValue}</div>
      <div className={styles.changeRate}>
        <span style={{ color: getRateColor() }} className={styles.change}>
-         {changeRate !== 0 &&
-           (changeRate >= 0 ? <ArrowUpwardIcon /> : <ArrowDownwardIcon />)}
+         <ChangeIndicator changeRate={changeRate} />
          {getChangeRate()}
        </span>
        &nbsp;vs last month
      </div>
    </div>
  );
backend/src/test/mocks/guidelog.mock.js (1)

4-11: The constructor's looking sharp, but it could use some validation, eh!

Let's add some type checking to ensure we're getting the right kinds of values.

  constructor(id, guideType) {
+   if (!Number.isInteger(id) || id < 1) {
+     throw new Error('ID must be a positive integer');
+   }
+   if (!Object.values(GuideType).includes(guideType)) {
+     throw new Error('Invalid guide type');
+   }
    this.guideLog = {
      guideType,
      guideId: id,
      userId: "1",
backend/src/test/unit/controllers/statistics.test.js (1)

18-28: He's nervous, but on the surface the tests look calm and ready! 🎯

The success case test needs more assertions on the response structure.

Add structure validation:

 expect(status).to.be.equal(200);
-expect(json).to.be.equal(guideLogList);
+expect(json).to.be.an('array');
+json.forEach(stat => {
+  expect(stat).to.have.all.keys(['views', 'change', 'guideType']);
+  expect(stat.views).to.be.a('number');
+  expect(stat.change).to.be.a('number');
+  expect(stat.guideType).to.be.a('string');
+});
frontend/src/scenes/dashboard/Dashboard.jsx (1)

10-27: Yo dawg, this switch case is straight fire! 🔥

The mapMetricName function is clean and handles all guide types effectively. However, we could make it even more robust.

Consider this enhancement to make it more maintainable:

+const GUIDE_TYPE_LABELS = {
+  popup: "Popups views",
+  hint: "Hints views",
+  banner: "Banners views",
+  link: "Links views",
+  tour: "Tours views",
+  checklist: "Checklists views",
+};

 const mapMetricName = (guideType) => {
-  switch (guideType) {
-    case "popup":
-      return "Popups views";
-    case "hint":
-      return "Hints views";
-    case "banner":
-      return "Banners views";
-    case "link":
-      return "Links views";
-    case "tour":
-      return "Tours views";
-    case "checklist":
-      return "Checklists views";
-    default:
-      return "Unknown";
-  }
+  return GUIDE_TYPE_LABELS[guideType] || "Unknown";
 };
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7448e08 and 1835edf.

📒 Files selected for processing (12)
  • backend/migrations/20241219181037-add-index-guidelog.js (1 hunks)
  • backend/src/controllers/statistics.controller.js (1 hunks)
  • backend/src/models/GuideLog.js (1 hunks)
  • backend/src/routes/statistics.routes.js (1 hunks)
  • backend/src/server.js (2 hunks)
  • backend/src/service/statistics.service.js (1 hunks)
  • backend/src/test/mocks/guidelog.mock.js (2 hunks)
  • backend/src/test/unit/controllers/statistics.test.js (1 hunks)
  • backend/src/test/unit/services/statistics.test.js (1 hunks)
  • frontend/src/scenes/dashboard/Dashboard.jsx (2 hunks)
  • frontend/src/scenes/dashboard/HomePageComponents/StatisticCards/StatisticCards.jsx (1 hunks)
  • frontend/src/services/statisticsService.js (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • backend/src/models/GuideLog.js
🧰 Additional context used
🪛 Biome (1.9.4)
backend/migrations/20241219181037-add-index-guidelog.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

🔇 Additional comments (4)
backend/src/routes/statistics.routes.js (2)

1-4: Yo, these imports are straight fire! 🔥

The essential dependencies are properly imported, and the authentication middleware is included - that's crucial for keeping our stats secure, dawg!


5-7: Secure setup with authentication - respect! 💯

The router configuration with authentication middleware ensures all routes are protected. That's the way we roll!

backend/migrations/20241219181037-add-index-guidelog.js (1)

12-14: Beauty! The index looks solid as a hockey puck!

The index addition is well-structured and follows naming conventions. Just verify if this index will be used effectively by your queries.

✅ Verification successful

Yo, this index is gonna be droppin' hot performance beats!

The index on showingTime is absolutely crucial, eh! Found solid evidence it's used in:

  • Date range filtering in statistics.service.js for monthly view comparisons
  • Timestamp tracking in guidelog.service.js for new entries
  • Already defined in the model with proper indexing configuration

The index will definitely improve query performance for these date-based operations, especially those monthly stats calculations. You're on fire with this one! 🔥

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the showingTime column is used in WHERE clauses or ORDER BY
rg -l "showingTime|showing_time.*WHERE|ORDER\s+BY" --type js

Length of output: 332


Script:

#!/bin/bash
# Let's check how showingTime is actually used in queries
rg "showingTime|showing_time" -B 2 -A 2 backend/src/service/guidelog.service.js
rg "showingTime|showing_time" -B 2 -A 2 backend/src/service/statistics.service.js

# Check the model definition to understand the column usage
rg "showingTime|showing_time" -B 2 -A 2 backend/src/models/GuideLog.js

Length of output: 996

backend/src/server.js (1)

26-26: Knees weak, but this route addition is strong! 💪

The statistics route integration follows the established pattern and is protected by the existing middleware stack.

Let's verify the route protection:

Also applies to: 61-61

✅ Verification successful

Mom's spaghetti is secure! The route is properly protected! 🍝

The statistics route is indeed locked down tight with the authenticateJWT middleware applied at the router level. Found the evidence right there in statistics.routes.js:

router.use(authenticateJWT)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the statistics route is protected by auth middleware

# Search for auth middleware in the statistics routes
rg -A 5 "router\.(get|post|put|delete).*statistics" ./backend/src/routes/

Length of output: 280


Script:

#!/bin/bash
# Let's check if there's any middleware setup in the main server.js file
rg -A 5 "app\.use.*statistics" ./backend/src/server.js

# Also check the route definition and imports in statistics routes
cat ./backend/src/routes/statistics.routes.js

# And let's see if there's a common middleware setup
rg "app\.use.*auth" ./backend/src/server.js

Length of output: 681

backend/src/controllers/statistics.controller.js Outdated Show resolved Hide resolved
backend/src/controllers/statistics.controller.js Outdated Show resolved Hide resolved
frontend/src/services/statisticsService.js Outdated Show resolved Hide resolved
backend/src/test/unit/services/statistics.test.js Outdated Show resolved Hide resolved
backend/src/test/unit/services/statistics.test.js Outdated Show resolved Hide resolved
backend/src/test/unit/controllers/statistics.test.js Outdated Show resolved Hide resolved
frontend/src/scenes/dashboard/Dashboard.jsx Outdated Show resolved Hide resolved
frontend/src/scenes/dashboard/Dashboard.jsx Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1835edf and be5b35c.

📒 Files selected for processing (5)
  • backend/src/controllers/statistics.controller.js (1 hunks)
  • backend/src/service/statistics.service.js (1 hunks)
  • backend/src/test/unit/services/statistics.test.js (1 hunks)
  • frontend/src/scenes/dashboard/Dashboard.jsx (2 hunks)
  • frontend/src/services/statisticsService.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/services/statisticsService.js
  • frontend/src/scenes/dashboard/Dashboard.jsx
  • backend/src/controllers/statistics.controller.js
🔇 Additional comments (2)
backend/src/service/statistics.service.js (2)

22-35: 🛠️ Refactor suggestion

Knees weak: Redundant type check in reducer! 💪

The guideType check in the reducer is redundant since we're already filtering by guideType in the query.

 const { thisMonthViews, lastMonthViews } = logs.reduce(
   (acc, log) => {
-    if (log.guideType !== guideType) {
-      return acc;
-    }
     if (log.showingTime >= thisMonth) {
       acc.thisMonthViews += 1;
     } else if (log.showingTime >= lastMonth) {
       acc.lastMonthViews += 1;
     }
     return acc;
   },
   { thisMonthViews: 0, lastMonthViews: 0 }
 );

Likely invalid or redundant comment.


13-21: ⚠️ Potential issue

Mom's spaghetti moment: Missing pagination could make the server heavy! 🍜

The findAll without limits could cause memory issues with large datasets.

Add pagination:

 const logs = await GuideLog.findAll({
   where: {
     guideType: Number(guideType),
     userId,
     showingTime: {
       [db.Sequelize.Op.gte]: twoMonthsAgo,
     },
   },
+  limit: 1000,
+  order: [['showingTime', 'DESC']],
 });

Likely invalid or redundant comment.

backend/src/test/unit/services/statistics.test.js Outdated Show resolved Hide resolved
backend/src/service/statistics.service.js Outdated Show resolved Hide resolved
backend/src/service/statistics.service.js Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
backend/src/service/statistics.service.js (1)

14-22: 🛠️ Refactor suggestion

Knees weak: Missing pagination and index, eh! 🍁

While the query includes proper filtering, consider these performance improvements:

  1. Add an index for the frequently queried columns
  2. Implement pagination to handle large datasets

Add an index and pagination:

+ // In your migration file
+ await queryInterface.addIndex('GuideLogs', ['userId', 'guideType', 'showingTime']);

 const logs = await GuideLog.findAll({
   where: {
     guideType: Number(guideType),
     userId,
     showingTime: {
       [db.Sequelize.Op.gte]: twoMonthsAgo,
     },
   },
+  limit: 1000,
 });
🧹 Nitpick comments (1)
backend/src/service/statistics.service.js (1)

23-36: Mom's spaghetti: Redundant check detected! 🍁

The guideType check in the reducer is redundant since the query already filters by guideType.

Simplify the reducer:

 const { thisMonthViews, lastMonthViews } = logs.reduce(
   (acc, log) => {
-    if (log.guideType !== guideType) {
-      return acc;
-    }
     if (log.showingTime >= thisMonth) {
       acc.thisMonthViews += 1;
     } else if (log.showingTime >= lastMonth) {
       acc.lastMonthViews += 1;
     }
     return acc;
   },
   { thisMonthViews: 0, lastMonthViews: 0 }
 );
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be5b35c and b737cee.

📒 Files selected for processing (1)
  • backend/src/service/statistics.service.js (1 hunks)
🔇 Additional comments (5)
backend/src/service/statistics.service.js (5)

1-5: LGTM! Clean imports and class structure, eh! 🍁

The imports and class declaration are well-organized and follow best practices.


8-11: Yo, these date calculations are looking fresh now! 🍁

The implementation properly uses month boundaries instead of fixed day offsets, addressing previous concerns about inconsistent results across months.


37-42: Arms are heavy: Percentage calculation needs more edge cases! 🍁

While the division by zero case is handled, consider making the calculation more explicit and handling the case when thisMonthViews is 0.

Improve edge case handling:

-const percentageDifference =
-  lastMonthViews === 0
-    ? 0
-    : Math.round(
-        ((thisMonthViews - lastMonthViews) / lastMonthViews) * 100
-      );
+const percentageDifference = (() => {
+  if (lastMonthViews === 0) return thisMonthViews > 0 ? 100 : 0;
+  if (thisMonthViews === 0) return -100;
+  return Math.round(((thisMonthViews - lastMonthViews) / lastMonthViews) * 100);
+})();

43-49: Clean result formatting, eh! 🍁

The result object is well-structured and maintains consistent casing.


51-54: Palms are sweaty: Improve error logging! 🍁

Replace console.log with a proper logging system for better error tracking in production.

Consider using a logging library:

-console.log(error);
+logger.error('Failed to generate statistics', { error, userId });

@gorkem-bwl
Copy link
Contributor

Looks good to me! Thanks Debora!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
backend/src/test/unit/controllers/statistics.test.js (2)

1-7: Yo, let's clean up these imports, they're making me nervous! 🍝

Consider destructuring beforeEach along with the other test lifecycle hooks in line 1:

-const { describe, it, afterEach } = require("mocha");
+const { describe, it, beforeEach, afterEach } = require("mocha");

17-28: The success test is straight fire, but we can make it hotter! 🔥

The test covers the basics well, but let's add some spice:

  1. Verify service was called with correct user ID
  2. Add assertions for response structure
 it("should return status 200 and the statistics to all guides", async () => {
   serviceMock.generateStatistics = sinon
     .stub(service, "generateStatistics")
     .resolves(guideLogList);
   await controller.getStatistics(req, res);
+  expect(serviceMock.generateStatistics.calledWith(req.user.id)).to.be.true;
   const status = res.status.firstCall.args[0];
   const json = res.json.firstCall.args[0];
   expect(status).to.be.equal(200);
   expect(json).to.be.equal(guideLogList);
+  expect(Array.isArray(json)).to.be.true;
 });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b737cee and f2507a8.

📒 Files selected for processing (3)
  • backend/src/test/e2e/statistics.test.mjs (1 hunks)
  • backend/src/test/unit/controllers/statistics.test.js (1 hunks)
  • backend/src/test/unit/services/statistics.test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/src/test/unit/services/statistics.test.js
🔇 Additional comments (1)
backend/src/test/unit/controllers/statistics.test.js (1)

29-41: Dropping bombs on error cases, but he keeps on forgetting! 💣

The error test case is too simple. We need to test specific error scenarios.

backend/src/test/e2e/statistics.test.mjs Show resolved Hide resolved
backend/src/test/e2e/statistics.test.mjs Show resolved Hide resolved
backend/src/test/e2e/statistics.test.mjs Show resolved Hide resolved
Copy link
Collaborator

@erenfn erenfn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can merge this after #401

@erenfn erenfn merged commit 297ae8d into develop Dec 20, 2024
2 checks passed
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.

Statistics for the Statistic cards
4 participants