Personal Fitness Tracker

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.
- activityID (
- 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.
- date (
- 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 usingremove_if
anderase
on the logs vector. - Logging Workouts:
logActivity
validates the activity ID then creates a newLogEntry
withmake_unique
, capturing the shared pointer to the activity so that activity metadata remains synchronized if updated later. - Querying Entries:
getEntriesByDate
andgetEntriesByActivity
iterate overlogs
, comparing either the date string or theactivityID
, then collect raw pointers (get()
) for non-owning access by the caller. - Report Generation:
generateReport
builds a transient map ofactivityID
to total amount within the date range, using lexicographical string comparisons for dates (valid given ISO format). It then looks up each activity inactivities
to print human-readable names and units. - Display Routines:
displayAllActivities
anddisplayAllLogs
simply iterate through their containers, invoking each object’sdisplay()
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]