|

Personal Fitness Tracker

Spread the love

Project Description

Create a C++ console application to help users log and monitor daily fitness activities. The system should support recording workouts, tracking progress over time, and generating summary reports. Required features:

  • Activity Representation: Implement an Activity class with:
    • activityID (int): a unique numeric identifier for each type of exercise (e.g., running, cycling).
    • name (std::string): descriptive name of the activity.
    • unit (std::string): measurement unit (e.g., “miles”, “minutes”, “reps”).
    • Method display(): outputs activity details.
  • Log Entry Representation: Implement a LogEntry class containing:
    • date (std::string): date of the workout (YYYY-MM-DD).
    • activity (std::shared_ptr<Activity>): pointer to the activity performed.
    • amount (double): quantity performed (e.g., distance, duration, count).
    • Method display(): prints the date, activity name, and amount.
  • FitnessTracker Management: A FitnessTracker class to manage activities and log entries:
    • addActivity(activityID, name, unit): register a new activity type, preventing duplicates.
    • removeActivity(activityID): delete an activity and all associated log entries.
    • logActivity(date, activityID, amount): add a new LogEntry for a given activity on a date.
    • getEntriesByDate(date): return all entries logged on a particular date.
    • getEntriesByActivity(activityID): return all entries for a specific activity.
    • generateReport(startDate, endDate): summarize total amounts per activity over a date range.
    • displayAllActivities() and displayAllLogs(): list registered activities and all log entries.
  • Behavioral Requirements:
    • Validate inputs: dates must follow the YYYY-MM-DD format; activityID must exist when logging.
    • Use std::vectorstd::unique_ptr for log entries and std::shared_ptr for activities.
    • Ensure memory safety with automatic cleanup and avoid raw new/delete.

Solution :

Activity class :
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
#include <string>
#include <map>
#include <iomanip>

// Activity class
class Activity {
public:
    int activityID;
    std::string name;
    std::string unit;

    Activity(int id, const std::string& n, const std::string& u)
        : activityID(id), name(n), unit(u) {}

    void display() const {
        std::cout << "ID: " << activityID
                  << " | " << name
                  << " (unit: " << unit << ")\n";
    }
};
LogEntry class :
// LogEntry class
class LogEntry {
public:
    std::string date;
    std::shared_ptr<Activity> activity;
    double amount;

    LogEntry(const std::string& d,
             const std::shared_ptr<Activity>& act,
             double amt)
        : date(d), activity(act), amount(amt) {}

    void display() const {
        std::cout << date << " - "
                  << activity->name << ": "
                  << amount << " " << activity->unit << "\n";
    }
};
FitnessTracker class :
// FitnessTracker class
class FitnessTracker {
    std::map<int, std::shared_ptr<Activity>> activities;
    std::vector<std::unique_ptr<LogEntry>> logs;

public:
    void addActivity(int id, const std::string& name, const std::string& unit) {
        if (activities.count(id)) {
            std::cerr << "Activity ID " << id << " already exists\n";
            return;
        }
        activities[id] = std::make_shared<Activity>(id, name, unit);
        std::cout << "Added activity: " << name << "\n";
    }

    void removeActivity(int id) {
        if (!activities.count(id)) {
            std::cerr << "Activity ID " << id << " not found\n";
            return;
        }
        activities.erase(id);
        logs.erase(
            std::remove_if(logs.begin(), logs.end(),
                [&](const auto& log){ return log->activity->activityID == id; }),
            logs.end()
        );
        std::cout << "Removed activity " << id << " and its logs\n";
    }

    void logActivity(const std::string& date, int id, double amount) {
        auto it = activities.find(id);
        if (it == activities.end()) {
            std::cerr << "Invalid activity ID\n";
            return;
        }
        logs.push_back(std::make_unique<LogEntry>(date, it->second, amount));
        std::cout << "Logged " << amount << " " << it->second->unit
                  << " on " << date << "\n";
    }

    std::vector<LogEntry*> getEntriesByDate(const std::string& date) const {
        std::vector<LogEntry*> result;
        for (const auto& log : logs) {
            if (log->date == date) result.push_back(log.get());
        }
        return result;
    }

    std::vector<LogEntry*> getEntriesByActivity(int id) const {
        std::vector<LogEntry*> result;
        for (const auto& log : logs) {
            if (log->activity->activityID == id) result.push_back(log.get());
        }
        return result;
    }

    void generateReport(const std::string& startDate,
                        const std::string& endDate) const {
        std::map<int, double> totals;
        for (const auto& log : logs) {
            if (log->date >= startDate && log->date <= endDate) {
                totals[log->activity->activityID] += log->amount;
            }
        }
        std::cout << "\nReport from " << startDate
                  << " to " << endDate << ":\n";
        for (auto& [id, total] : totals) {
            auto act = activities.at(id);
            std::cout << act->name << ": "
                      << total << " " << act->unit << "\n";
        }
    }

    void displayAllActivities() const {
        std::cout << "\nActivities:\n";
        for (auto& [id, act] : activities) act->display();
    }

    void displayAllLogs() const {
        std::cout << "\nAll Logs:\n";
        for (const auto& log : logs) log->display();
    }
};


Detailed Explanation of Logic

The FitnessTracker organizes Activity instances in a std::map<int, shared_ptr<Activity>>, keyed by activityID for O(log N) lookup. Log entries live in a std::vector<unique_ptr<LogEntry>>, ensuring automatic cleanup.

  • Adding/Removing Activities: addActivity guards against duplicate IDs. removeActivity erases the activity from the map and then prunes all log entries associated with that ID using remove_if and erase on the logs vector.
  • Logging Workouts: logActivity validates the activity ID then creates a new LogEntry with make_unique, capturing the shared pointer to the activity so that activity metadata remains synchronized if updated later.
  • Querying Entries: getEntriesByDate and getEntriesByActivity iterate over logs, comparing either the date string or the activityID, then collect raw pointers (get()) for non-owning access by the caller.
  • Report Generation: generateReport builds a transient map of activityID to total amount within the date range, using lexicographical string comparisons for dates (valid given ISO format). It then looks up each activity in activities to print human-readable names and units.
  • Display Routines: displayAllActivities and displayAllLogs simply iterate through their containers, invoking each object’s display() for clean separation of data and presentation.

This design leverages modern C++ memory management to keep ownership explicit, provide efficient lookups, and maintain synchronization between activity definitions and their logged usage.

If you have any questions , feel free to contact us at [email protected]

Similar Posts