Exercise Tracker works but not passing

Hey Guys;
My Exercise tracker works great, but fails most of the tests.
I wonder what the tests are looking for - perhaps the problem is with using object id as _id/userId, or is it with the Schema? not sure.
I included all my code -works great, but won’t pass the final 3 tests.
Any ideas?
Thanks :slight_smile:

const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
const mongoose = require("mongoose");
mongoose.connect(process.env.DB_URI); // || 'mongodb://localhost/exercise-track' )
// Make Mongoose use `findOneAndUpdate()`. Note that this option is `true`
// by default, you need to set it to false.
mongoose.set('useFindAndModify', false);
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

//define our schema:
const trackerSchema = new mongoose.Schema(
  {
    _id:{ type: mongoose.Schema.ObjectId, auto: true },
    username: String, // Schema for users and docs will be kept in []log
    date: Date,
    count: Number,
    log: [
      {
        // this  log[] holds all logs for each user
        username: String,
        description: String,
        duration: Number,
       _id: mongoose.Schema.ObjectId,
        date: Date
      }
    ]
  }
  
);
const TrackerModel = mongoose.model("TrackerModel", trackerSchema);
console.log(mongoose.connection.readyState);

//add static file - style.css
//app.use("/public", express.static(process.cwd() + "/public"));   isn't working with /public route

let ourUserArray = []; // this will hold our users from DB
let ourDocsArray = []; // this will hold our info for 'this' user
var finalDocArray = [];
//define our routes
app.use(express.static(process.cwd() + "/public"));
app.get("/", (req, res) => {
  res.sendFile(process.cwd() + "/views/index.html");
});

// Not found middleware
//app.use((req, res, next) => {
//  return next({status: 404, message:req.body.new+'not found'})
//})

// Error Handling middleware
app.use((err, req, res, next) => {
  let errCode, errMessage;
  if (err.errors) {
    // mongoose validation error
    errCode = 400; // bad request
    const keys = Object.keys(err.errors);
    // report the first validation error
    errMessage = err.errors[keys[0]].message;
  } else {
    // generic or custom error
    errCode = err.status || 500;
    errMessage = err.message || "Internal Server Error";
  }
  res
    .status(errCode)
    .type("txt")
    .send(errMessage);
});

// recieves submit data for user name- db will return _id to use for logging exercises
app.post("/api/exercise/new-user", async function(req, res) {
  const { username } = req.body; //destructure POST variables
  let existingUser = false;     // unless we find one
  if (username) {
    await TrackerModel
      .findOne({ username: username }) //find existing, else it's new user })
      .exec()
      .then(docs => {
        if (docs) {
          existingUser = true;
          console.log("Existing user " + docs.username + "FOUND " + docs._id);
          return res.json(docs);
        }
      })
      .catch(err => {
        console.log(err);
        res.send(err + "Couldn't access database to check for user ID");
      });
  }
  console.log("about to look up user " + username);
  console.log("connection State:" + mongoose.connection.readyState);

  //save new user's profile
  if (!existingUser) {
    let date = new Date(); // if no date given, use this date
    if (req.body.date) {
      date = req.body.date;
    }
    console.log("Schema creation at line 130");
    
    
    // Possible solution? -refactor my code to create objectId from string id of userName
    // can I cast this back and forth? - req._id will be that string?
    
    var _id= new mongoose.Types.ObjectId();  //creates our _id
    var tracker = new TrackerModel({
      _id,
      username: username,
      date: date, 
      count: 0,  // count keeps track of # of workout logs for this user
      log:[]
        // {
        //   userName: username,
        //   description: "create profile",
        //   duration: 0,
        //  _id,
        //   date
        // }
      
    });
    await tracker.save(err => {
      if (err) {
        return "error saving to data base" + err;
      } else{
        //res.json(tracker.userName, tracker._id);
  
        return res.send(tracker);
      }
    });
    
  
  }
});

// Get api/exercise/users to get an array of all users
app.get("/api/exercise/users/", async function(req, res) {
  let arrayOfUserDocs = [];
  let arrayOfUsers = [];
  await TrackerModel
    .find()
    .exec()
    .then(async docs => {
      arrayOfUserDocs.push(docs);
      arrayOfUserDocs = arrayOfUserDocs[0]; // array was in location[0]
      arrayOfUserDocs.forEach(user => {
        //for(var i=0; i<arrayOfUserDocs.length; i++){
        var thisUser = user.username;
        console.log(thisUser);
        if (arrayOfUsers.indexOf(thisUser) == -1) {
          arrayOfUsers.push(thisUser);
        }
        //} closing for loop
      }); // closing forEach used instead

      res.send(arrayOfUsers);
    })
    .catch(err => {
      console.log(err);
    });
});

// this is where the exercise is logged
app.post("/api/exercise/add", async function(req, res) {
  var username;
  let { userId, description, duration, date } = req.body;
  if (!date) {
    date = new Date();
  }
  //check if userId is valid
  if (!mongoose.Types.ObjectId.isValid(userId)) {
    res.send(
      userId +
        "please enter valid userId, use create new user to look up your userId"
    );
  }
  // get userName from userId
  await TrackerModel.findOne(
    {_id: userId})
    .exec()
    .then( async docs=>{
    username=docs.username;
  })
    .catch(err=> console.log("error occured while accessing DB looking up "+userId+" copy your userId again and retry"));
  
  
 //   console.log(
 //      "Line 209 about to get userName and save log. connection:" + mongoose.connection.readyState
 //   );
  var newLog = [{ username, description, duration, userId, date }];
  //add data verification here
  var newDoc;
  try{
    await TrackerModel
    .findByIdAndUpdate(
      { _id: userId },
      {
        $push: {
          log: newLog
        },
        $inc: {
          count: 1
        }
       },{'new': true, lean:true},
      function(err, result) {
      if (err) {
        res.send(err);
      } else {
        res.send(result);
      }
    }
    );
    //return res.json(newDoc);
  }
  catch (err){
         if (err){
           console.log("error line 245");
           res.status(500).send({ error: err.toString });
         }
           // return res.send( doc._id+ doc.log[doc.log.length-1]);  // now true: returns NEW doc-pulled out Log    
  }
}); // closes this api endpoint

//to get user logs  querry from url     ?userName=p_ollie
app.get("/api/exercise/log/:_id?/:from?/:to?/:limit?", async function(
  req,
  res
) {
  let ourUserName = "";
  let { _id, from, to, limit } = req.query; // load userName in URL query ?userN=tara
  //_id=new mongoose.Types.ObjectId(_id);
  let logCount = 0;
  if (!_id) {
    await TrackerModel
      .find()
      .exec()
      .then(docs => {
        return res.json({ id: "All", Logs: docs.length, docs }); // if no id, display all logs
      })
      .catch(err => {
        res.send(err);
      });
  }
  console.log(_id + from + to + limit);
  if (_id) {
    // if _id exists ensure it is valid
    if (!mongoose.Types.ObjectId.isValid(_id)) {
      res.send(
        _id +
          "please enter valid userId, use create new user to look up your userId"
      );
    }
    console.log(
      req.query._id +
        " passed ObjectId check - recieved request for logs for: " +
        _id +
        from +
        to +
        limit
    );

    // get userName from Db as all logs stored under user name
    // ie. each log get's it's own unique _id, so it's sorted by username
    await TrackerModel
      .findById(_id)
      .exec()
      .then(async docs => {
        console.log("looking  for userName and logs for userId:" + _id);
        if (docs) {
          console.log("docs found");
          ourUserName = docs.username; //pull userName from DB
          console.log("Docs are stored under user  " + ourUserName);
          // we will querry all logs with this username
        } else res.send("No files for user " + _id);
      })
      .catch(err => {
        res.send(err);
      });
  } // closes if(id)
  if (ourUserName) {
    // if we got it from DB find logs under that name
    await TrackerModel
      .find({
        username: ourUserName
      })
      .exec()
      .then(docs => {
        ourDocsArray = docs; // loads up our data into []
        logCount = docs[0].count
      });
  }
  if (!to && !from && !limit) {
    if (_id) {
      // if no parameters set, return all docs for user
      res.json(
         ourDocsArray
      );
    }
  }
 
  // first extract raw data into final array
  finalDocArray = ourDocsArray;
  if (to) {
    let toDate = new Date(to);
    let counter = finalDocArray.length;
    console.log("compare to date: " + to);
    for (var i = 0; i < counter; i++) {
      let docDate = new Date(finalDocArray[i].log.date);
      console.log(
        toDate +
          toDate.getTime() +
          " compare date to " +
          docDate +
          docDate.getTime()
      );
      if (toDate.getTime() < docDate.getTime()) {
        // if date is after 'to', delete that element
        finalDocArray.splice(i, 1);
        console.log("it worked item deleted");
        i--; // because we deleted this index next one slide here
        counter--; //now reduce length to reflect new array
        //}
      }
    }
  }
  if (from) {
    let fromDate = new Date(from);
    if (!to) {
      // if 'to' not set, get all records for user
      finalDocArray = ourDocsArray[0];
    }
    let tempArray = finalDocArray.filter(log => {
      let logDate = new Date(log.date);
      console.log(logDate);
      if (fromDate.getTime() < logDate.getTime()) {
        return true;
      } else return false;
    });
    finalDocArray = tempArray;
  }
  if (limit) {
    if (finalDocArray.length > limit) {
      console.log("trim results to meet limit " + limit);
      finalDocArray.length = limit;
    }
  }
 });

const listener = app.listen(process.env.PORT || 3000, () => {
  console.log("Your app is listening on port " + listener.address().port);
});

I just got mine to pass all tests, but I also struggled with failing tests despite my app behaving the way the project said it should. Unfortunately I have seen on github and on other FCC forum posts that the tests are apparently very specific but do not align perfectly with the wording of the project/challenge. But here are a few tips that helped me out in the last day or two that might also help you.

  • It seems to want the exercise dates to make use of toDateString(), I stumbled onto this idea somewhere along the way. Even though I saw some other users’ glitch projects that passed tests while just plain outputting the date? Anyway, I’m now calling toDateString() on my date in the /add endpoint before updating my MongoDB document.

  • I think that it also wants the duration to be an integer. Original I was entering decimal values like 67.05 but got better luck once I started using isNaN() and parseInt().

  • I started logging the request body/query on my POST endpoints so that I could watch the console while the tests ran. Once I could see what values the test cases were passing I could try manually passing the exact same thing to the endpoints.

  • Last but not least! When adding an exercise via the HTML form, there will always be a date field in the request body, although the value may be empty. But, I noticed one of the test cases hits the /add endpoint without supplying a date field in the POST body at all. So you need to test not just for an empty or undefined value, but the complete absence of date in the body of the request, which I only discovered using Postman instead of the HTML form.

5 Likes

Oh man , Im really struggling with the last test , the rest of them are passing. Could you take a look at my code ? Dont know where else to get some help , running out of options. Here is my glitch project link: https://glitch.com/~excersise-tracker333

Here are two things I’ve noticed by just adding a user, a few exercises, and making some requests to your api/exercise/log using different combinations and ordering of the to, from, and limit filters.

Your JSON object when calling the log also returns the to and from date parameters if any were supplied. While it might be a nice feature, the tests can be super picky so I wonder if omitting that from the output would help.

Also, I noticed that if I try to use the ‘limit’ filter without supplying any ‘to’ or ‘from’ dates, the limit is not applied and I will see all of that user’s exercises regardless. In your if-branches for date filtering, you apply the limit when the branches for the “from” and “to” parameters are executed, but not in your final else branch that executes when no date parameters were supplied. All 3 filter parameters are optional so you should be able to use all, none, or any combination of them. This is likely what’s failing the final test.

If you want to see what data the tests are actually sending to your API endpoints, you can log properties of the request body to the console in your API handlers, then, run the tests on the FCC challenge page and jump back over to your glitch tab to see what shows up in the console. That really helped me to get more insight into the actual test cases when debugging my own project.

1 Like

I love you man ,finally Its working fine , the last test was not passing because I forgot to filter logs if limit was applied in case from or to parameters were not specified. Thankyou so much , really , e-hug for you my man.

2 Likes