diff --git a/.gitignore b/.gitignore index e43b0f9..2765dfb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .DS_Store +node_modules/ +db/ diff --git a/README.md b/README.md index 61ac6cb..96afdae 100644 --- a/README.md +++ b/README.md @@ -79,9 +79,9 @@ The API you build should have the following capabilities. The schema of your dat - Given a customer's `id` and a movie's `title` ... - "check out" one of the movie's inventory to the customer - Establish a return date - - Charge the customer's account (cost up to you) - "check in" one of customer's rentals - return the movie to its inventory + - Charge the customer's account (cost up to you) - See a list of customers with overdue movies ### Interface diff --git a/app.js b/app.js new file mode 100644 index 0000000..9d73c7e --- /dev/null +++ b/app.js @@ -0,0 +1,64 @@ +var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + + +var app = express(); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'jade'); + +// uncomment after placing your favicon in /public +//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +var customers = require('./routes/customers'); +app.use('/customers', customers); + +var movies = require('./routes/movies'); +app.use('/movies', movies); + +var rent = require('./routes/rentals'); +app.use('/rent', rent); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handlers + +// development error handler +// will print stacktrace +if (app.get('env') === 'development') { + app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: err + }); + }); +} + +// production error handler +// no stacktraces leaked to user +app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {} + }); +}); + + +module.exports = app; diff --git a/bin/www b/bin/www new file mode 100755 index 0000000..cc43085 --- /dev/null +++ b/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var app = require('../app'); +var debug = require('debug')('C3Projects--VideoStoreAPI:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/controllers/customers.js b/controllers/customers.js new file mode 100644 index 0000000..d0afa2f --- /dev/null +++ b/controllers/customers.js @@ -0,0 +1,55 @@ +"use strict"; +var Customer = require("../models/customer.js"); + +exports.customersController = { + findAllCustomers: function(req, res) { + var dbCustomer = new Customer(); + var result = dbCustomer.find_all(function(err,result){ + return res.status(200).json(result); + }); + }, + + sortCustomersByName: function(req, res) { + var dbCustomer = new Customer(); + var limit = req.params.limit; + var offset = req.params.offset; + var result = dbCustomer.sort_by("name", limit, offset, function(err,result){ + return res.status(200).json(result); + }); + }, + + sortCustomersByRegisteredAt: function(req, res) { + var dbCustomer = new Customer(); + var limit = req.params.limit; + var offset = req.params.offset; + // console.log(res) + var result = dbCustomer.sort_by_registered_date("registered_at", limit, offset, function(err,result){ + return res.status(200).json(result); + }); + }, + + sortCustomersByPostalCode: function(req, res) { + var dbCustomer = new Customer(); + var limit = req.params.limit; + var offset = req.params.offset; + var result = dbCustomer.sort_by("postal_code", limit, offset, function(err,result){ + return res.status(200).json(result); + }); + }, + + getMoviesByCustomer: function(req, res) { + var dbCustomer = new Customer(); + var customerId = req.params.id; + dbCustomer.past_rentals_by_customer(customerId, function(err, result) { + res.status(200).json(result); + }); + }, + + getCurrentMoviesbyCustomer: function(req, res) { + var dbCustomer = new Customer(); + var customerId = req.params.id; + dbCustomer.current_checkout_rentals('customer_id', customerId, function(err, result) { + res.status(200).json(result); + }); + } +} diff --git a/controllers/movies.js b/controllers/movies.js new file mode 100644 index 0000000..56a5f03 --- /dev/null +++ b/controllers/movies.js @@ -0,0 +1,81 @@ +"use strict"; +var Movie = require("../models/movie.js"); +var Rental = require("../models/rental"); +var Customer = require("../models/customer"); + +exports.moviesController = { + findAllMovies: function(req, res) { + var dbMovie = new Movie(); + dbMovie.find_all(function(err,result){ + res.status(200).json(result); + }); + }, + + findMovieByTitle: function(req, res) { + var dbMovie = new Movie(); + var value = req.params.title; + console.log(value); + dbMovie.find_by("title", value, function(err,result){ + res.status(200).json(result); + }); + }, + + sortMoviesByTitle: function(req, res) { + var dbMovie = new Movie(); + var limit = req.params.limit; + var offset = req.params.offset; + dbMovie.sort_by("title", limit, offset, function(err,result){ + res.status(200).json(result); + }); + }, + + sortMoviesByReleaseDate: function(req, res) { + var dbMovie = new Movie(); + var limit = req.params.limit; + var offset = req.params.offset; + dbMovie.sort_by("release_date", limit, offset, function(err,result){ + res.status(200).json(result); + }); + }, + + currentCustomerRentals: function(req, res) { + var dbMovie = new Movie(); + var dbRental = new Rental(); + var dbCustomer = new Customer(); + var title = req.params.title; + dbMovie.getCurrentRentalCustomer(dbMovie, dbRental, dbCustomer, title, function(error, result) { + console.log(result); + res.status(200).json(result); + }); + }, + + pastCustomerRentals: function(req, res) { + var dbMovie = new Movie(); + var dbRental = new Rental(); + var dbCustomer = new Customer(); + var title = req.params.title; + var regex = /[^\/]+$/ + var sort = regex.exec(req.url) + dbMovie.getPastRentalCustomer(dbMovie, dbRental, dbCustomer, title, sort[0], function(error, result) { + res.status(200).json(result); + }); + + }, + + pastCustomerRentalsByDate: function(req, res) { + var dbMovie = new Movie(); + var title = req.params.title; + + dbMovie.getPastRentalCustomerByDate(dbMovie, title, function(error, result) { + res.status(200).json(result) + }); + }, + + availableMovies: function(req, res) { + var dbMovie = new Movie(); + var title = req.params.title; + dbMovie.available(title, function(err,result){ + res.status(200).json(result); + }); + } +} diff --git a/controllers/rentals.js b/controllers/rentals.js new file mode 100644 index 0000000..99aaf71 --- /dev/null +++ b/controllers/rentals.js @@ -0,0 +1,111 @@ +"use strict"; +var Rental = require("../models/rental"); +var Customer = require("../models/customer"); +var Movie = require("../models/movie"); + +exports.rentalsController = { + customersRentalHistory: function(req, res) { + var rental = new Rental(); + rental.customersRentalHistory(function(err,result){ + res.status(200).json(result); + }); + }, + + checkin: function(req, res) { + var rental = new Rental(); + var responseBody = Object.keys(req.body); + var dataString = responseBody[0]; + var data = JSON.parse(dataString); + + rental.checkin(data, function(err, result){ + res.status(200).json(result); + }); + }, + + checkout: function(req, res) { + var rental = new Rental(); + + rental.create_rental(req.body, function(error, result) { + res.status(200).json(result); + }); + }, + + customersOverdue: function(req, res) { + var rental = new Rental(); + rental.customersRentalHistory(function(err,result){ + var overdues = []; + var overdue = 0; + var overdueDays = 0; + var overdueMonths = 0; + + for(var i = 0; i < result.length; i ++) { + var returnedDate = result[i].returned_date; // "01-23-2015" + var checkoutDate = result[i].checkout_date; // "01-26-2015" + var rentalTime = result[i].rental_time; // "2" + + var checkoutDateArray = checkoutDate.split("-"); // ["01", "26", "2015"] + var checkoutMonth = checkoutDateArray[0]; // "01" + var checkoutDay = checkoutDateArray[1]; // "26" + + checkoutMonth = convertToDays(checkoutMonth); + + if(returnedDate != 'nil') { + var returnedDateArray = returnedDate.split("-"); // ["01", "23", "2015"] + var returnedMonth = returnedDateArray[0]; + var returnedDay = returnedDateArray[1]; + + returnedMonth = convertToDays(returnedMonth); + + overdueMonths = returnedMonth - checkoutMonth; + overdueDays = returnedDay - checkoutDay; + overdue = overdueMonths + overdueDays - rentalTime; + overdue = overdue < 0 ? 0:overdue; + + pushCustomerToArrayIfOverdue(result[i], overdue, overdues); + + } else { + var date = new Date(); + var dateString = date.getMonth() + "-" + date.getDate() + "-" + date.getFullYear(); + var dateStringArray = dateString.split("-"); + + var currentMonth = dateStringArray[0]; + var currentDay = dateStringArray[1]; + + currentMonth = convertToDays(currentMonth); + + overdueMonths = currentMonth - checkoutMonth; + overdueDays = currentDay - checkoutDay; + overdue = overdueMonths + overdueDays - rentalTime; + overdue = overdue < 0 ? 0:overdue; + + pushCustomerToArrayIfOverdue(result[i], overdue, overdues); + } + } + res.status(200).json(overdues); + }); + } +} + function convertToDays(arg){ + var arg = arg; + + if (arg == "04" || arg == "06" || arg == "09" || arg == "11"){ + arg = "30"; + }else if (arg == "02") { + arg = "28"; + }else { + arg = "31"; + } + return arg; + } + + function pushCustomerToArrayIfOverdue(arg, overdue, overdues) { + var overdue = overdue; + var overdues = overdues; + + if (overdue > 0) { + var customer = arg.name; + var overdueInfo = {}; + overdueInfo[customer] = overdue; + overdues.push(overdueInfo); + } + } diff --git a/database.js b/database.js new file mode 100644 index 0000000..61abd4f --- /dev/null +++ b/database.js @@ -0,0 +1,159 @@ +"use strict"; + +var sqlite3 = require("sqlite3").verbose(), + db_env = process.env.DB || 'development'; + +module.exports = { + find_by: function(column, value, callback) { + var db = new sqlite3.Database('db/' + db_env + '.db'); + var statement = "SELECT * FROM " + this.table_name + " WHERE " + column + " = ?;"; + + db.all(statement, value, function(err, res) { + if (callback) callback(err, res); + db.close(); + }); + }, + + find_all: function(callback) { + var db = new sqlite3.Database('db/' + db_env + '.db'); + var statement = "SELECT * FROM " + this.table_name + ";"; + + db.all(statement, function(err, res) { + if (callback) callback(err, res); + db.close(); + }); + }, + + sort_by: function(column, limit, offset, callback){ + var db = new sqlite3.Database('db/' + db_env + '.db'); + var statement = "SELECT * FROM " + this.table_name + " ORDER BY " + column + " LIMIT " + limit + " OFFSET " + offset + ";"; + + db.all(statement, function(err, res) { + if (callback) callback(err, res); + db.close(); + }); + }, + + sort_by_registered_date: function(column, limit, offset, callback){ + var db = new sqlite3.Database('db/' + db_env + '.db'); + var statement = "SELECT * FROM " + this.table_name + " ORDER BY " + column + " LIMIT " + limit + " OFFSET " + offset + ";"; + + db.all(statement, function(err, res) { + res = res.sort(function(a, b) { return Date.parse(a.registered_at) > Date.parse(b.registered_at) }); + if (callback) callback(err, res); + db.close(); + }); + }, + + available: function(value, callback){ + var db = new sqlite3.Database('db/' + db_env + '.db'); + var statement = "SELECT title, available FROM " + this.table_name + " WHERE title = ?;"; + + db.all(statement, value, function(err, res) { + if (callback) callback(err, res); + db.close(); + }); + }, + + current_checkout_rentals: function(column, value, callback) { + var db = new sqlite3.Database('db/' + db_env + '.db'); + var statement = "SELECT * FROM rentals WHERE " + column + " = ? AND returned_date = 'nil';"; + + db.all(statement, value, function(error, result) { + + if (callback) callback(error, result); + db.close(); + }) + }, + + past_checkout_rentals: function(column, value, callback) { + var db = new sqlite3.Database('db/' + db_env + '.db'); + var statement = "SELECT * FROM rentals WHERE " + column + " = ? AND returned_date != 'nil';"; + + db.all(statement, value, function(error, result) { + if (callback) callback(error, result); + db.close(); + }) + }, + + past_checkout_rentals_by_date: function(value, callback) { + var db = new sqlite3.Database('db/' + db_env + '.db'); + var statement = "SELECT name, registered_at, address, city, state, postal_code, phone, account_credit, checkout_date FROM customers, rentals WHERE customers.id = rentals.customer_id AND rentals.movie_id = " + value +" AND rentals.returned_date != 'nil' ORDER BY checkout_date DESC;"; + + db.all(statement, function(error, result) { + if (callback) callback(error, result); + db.close(); + }) + }, + + past_rentals_by_customer: function(value, callback) { + var db = new sqlite3.Database('db/' + db_env + '.db'); + var statement = "SELECT title, checkout_date, returned_date FROM movies, rentals where movies.id = rentals.movie_id and rentals.returned_date != 'nil' and rentals.customer_id = " + value + " ORDER BY checkout_date DESC;"; + + db.all(statement, function(error, result) { + console.log(statement); + if (callback) callback(error, result); + console.log(result); + db.close(); + }) + }, + + create_rental: function(data, callback) { + var db = new sqlite3.Database('db/' + db_env + '.db'); + var returned_date = null, + total = null; + var statement = "INSERT INTO rentals (checkout_date, returned_date, rental_time, \ + cost, total, customer_id, movie_id) \ + VALUES (?, ?, ?, ?, ?, ?, ?);"; + + var return_statement = "SELECT * FROM rentals WHERE ID = (SELECT MAX(ID) FROM rentals);"; + + var values = [] + values.push(data.checkout_date); + values.push(returned_date); + values.push(data.rental_time); + values.push(data.cost); + values.push(total); + values.push(data.customer_id); + values.push(data.movie_id); + + + db.run(statement, values, function(error, result) { + callback(error, this); + db.close(); + }); + + }, + + customersRentalHistory: function(callback){ + var db = new sqlite3.Database('db/' + db_env + '.db'); + var statement = "SELECT name, checkout_date, returned_date, rental_time FROM customers, rentals WHERE customers.id = rentals.customer_id ORDER BY checkout_date DESC;"; //[ { customer_id: 1 }, { customer_id: 2 }, { customer_id: 3 }, { customer_id: 4 } ] + db.all(statement, function(err, res) { + if (callback) callback(err, res); + db.close(); + }); + }, + + checkin: function(data, callback) { + var db = new sqlite3.Database('db/' + db_env + '.db'); + var customer_id = data.customer_id; // 2 + var movie_id = data.movie_id; // 2 + var total = data.total; // 5 + var returned_date = data.returned_date; // "09-20-2015" + + var rentalsStatement = "UPDATE rentals SET total = " + total + ", returned_date = '" + returned_date + "' WHERE customer_id = " + customer_id + " AND movie_id = " + movie_id + ";"; + var customersStatement = "UPDATE customers SET account_credit = account_credit - (SELECT total FROM rentals WHERE customers.id = rentals.customer_id AND rentals.movie_id = " + movie_id + ");"; + var moviesStatement = "UPDATE movies SET available = available + 1 WHERE id = " + movie_id + ";"; + + db.serialize(function(){ + db.exec("BEGIN"); + db.exec(rentalsStatement); + db.exec(customersStatement); + db.exec(moviesStatement) + db.exec("COMMIT", function(error) { + callback(error, "Success"); + db.close(); + }); + }) + } +} diff --git a/models/customer.js b/models/customer.js new file mode 100644 index 0000000..6de3cc8 --- /dev/null +++ b/models/customer.js @@ -0,0 +1,8 @@ +"use strict"; + +function Customer() { + this.table_name = "customers"; +} + +Customer.prototype = require('../database'); +module.exports = Customer; diff --git a/models/movie.js b/models/movie.js new file mode 100644 index 0000000..a46d575 --- /dev/null +++ b/models/movie.js @@ -0,0 +1,105 @@ +"use strict"; + +function Movie() { + this.table_name = "movies"; + +} + +Movie.prototype = require('../database'); + +Movie.prototype.getCurrentRentalCustomer = function(movieDb, rentalDb, customerDb, title, callback) { + getMovieId(movieDb, title, function(error, movie_id) { + getCustomerIdCurrent(rentalDb, movie_id[0].id, function(error, rentalInstances) { + var customers = [] + for (var i = 0; i < rentalInstances.length; i++) { + getCustomer(customerDb, rentalInstances[i].customer_id, function(error, customerInstance) { + customers.push(customerInstance[0]); + if (customers.length == rentalInstances.length) { + callback(error, customers); + } + }); + } + }); + }); +} + +Movie.prototype.getPastRentalCustomer = function(movieDb, rentalDb, customerDb, title, sort, callback) { + getMovieId(movieDb, title, function(error, movieId) { + getCustomerIdPast(rentalDb, movieId[0].id, function(error, rentalInstances) { + var customers = []; + for (var i = 0; i < rentalInstances.length; i++) { + getCustomer(customerDb, rentalInstances[i].customer_id, function(error, customerInstance) { + customers.push(customerInstance[0]); + if (customers.length == rentalInstances.length) { + if (sort == "sort_by_id") { + callback(error, sortById(customers)); + } + else if (sort == "sort_by_name") { + callback(error, sortByName(customers)); + } + } + }); + } + }); + }); +} + +Movie.prototype.getPastRentalCustomerByDate = function(movieDb, title, callback) { + getMovieId(movieDb, title, function(error, movieId) { + movieDb.past_checkout_rentals_by_date(movieId[0].id, function(error, customers) { + callback(error, customers); + }); + }); +} + +function sortById(array) { + array.sort(function(a, b) { + if (a.id > b.id) { + return 1; + } + if (a.id < b.id) { + return -1; + } + return 0; + }); + return array; +} + +function sortByName(array) { + array.sort(function(a, b) { + if (a.name.toLowerCase() > b.name.toLowerCase()) { + return 1; + } + if (a.name.toLowerCase() < b.name.toLowerCase()) { + return -1; + } + return 0 + }) + + return array; +} + +function getMovieId(instance, title, callback) { + instance.find_by("title", title, function(error, result) { + callback(error, result); + }) +} + +function getCustomerIdCurrent(instance, movieId, callback) { + instance.current_checkout_rentals("movie_id", movieId, function(error, result) { + callback(error, result); + }) +} + +function getCustomerIdPast(instance, movieId, callback) { + instance.past_checkout_rentals("movie_id", movieId, function(error, result) { + callback(error, result); + }) +} + +function getCustomer(instance, customerId, callback) { + instance.find_by("id", customerId, function(error, result) { + callback(error, result); + }) +} +module.exports = Movie; diff --git a/models/rental.js b/models/rental.js new file mode 100644 index 0000000..7c16c10 --- /dev/null +++ b/models/rental.js @@ -0,0 +1,9 @@ +"use strict"; + +function Rental() { + this.table_name = "rentals"; +} + +Rental.prototype = require('../database'); + +module.exports = Rental; diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..510e451 --- /dev/null +++ b/notes.md @@ -0,0 +1,115 @@ +Movies + +http://localhost:3000/movies +πŸ”΅ get "/movies" = All movies + - written method 'find_all'; test passed, + - displays all movies on webpage + +http://localhost:3000/movies/sort/title/5/0 +πŸ”΅ get "/movies/sort/title/:limit/:page" = sort title alphabetically. We need movie titles. + * From Movies table: + - titles + +http://localhost:3000/movies/sort/release_date/5/0 +πŸ”΅ get "/movies/sort/release_date/:limit/:page" = sort by release_date. We need movie release dates. + * From Movies table: + - release_date + +http://localhost:3000/movies/The%20Silence%20of%20the%20Lambs/customers/current +πŸ”΅ get "/movies/:title/customers/current" = Customers who currently have this movie checked out. + - From Movies table find movie_id + - From rentals table find all the same movie_ids + - if returned_date == nil + - return customer_id + - From Customer table find customers with customer_id + +http://localhost:3000/movies/The%20Exorcist/customers/past/sort_by_id +πŸ”΅ get "/movies/:title/customers/past/sort_by_id" = movie title, sorted by customers ids + - From Movies Table find movie_id + - From Rentals table find all the same movie_ids + - if returned_date != nil + - return customer_ids + - From Customers table find customers with customer_id. + +http://localhost:3000/movies/The%20Exorcist/customers/past/sort_by_name +πŸ”΅ get "/movies/:title/customers/past/sort_by_name" = movie title, sorted by customers names + +http://localhost:3000/movies/The%20Exorcist/customers/past/sort_by_checkout_date +πŸ”΅ get "/movies/:title/customers/past/sort_by_checkout_date" = movie title, sorted by checkout dates + +http://localhost:3000/movies/The%20Exorcist +πŸ”΅ get "/movies/:title" = Search for one specific title. Include synopsis, release date, and inventory total. + - From Movies table find title + +http://localhost:3000/movies/The%20Exorcist/available +πŸ”΅ get "/movies/:title/available" = Returns inventory available to rent. + - From Movies table find available + +Customers +http://localhost:3000/customers +πŸ”΅ get "/customers" = All customers + - √ From Customers table find all customers + - √ displays all customers on webpage + +http://localhost:3000/customers/sort/name/5/0 +πŸ”΅ get "/customers/sort/name/:limit/:page" = sort customers alphabetically by name. + - From Customers table find all customers + - sort by name + +http://localhost:3000/customers/sort/registered_at/5/0 +πŸ”΅ get "/customers/sort/registered_at/:limit/:page" = returns all customers sorted date they registered. + +http://localhost:3000/customers/sort/postal_code/5/0 +πŸ”΅ get "/customers/sort/postal_code/:limit/:page" = returns all customers sorted by postal_code. + +CHECK +get "/customers/:id/movies/current" = show all the movies that this customer is renting currently. + - From Rentals table find all of one customer's current rentals + +http://localhost:3000/customers/1/movies/past +πŸ”΅ get "/customers/:id/movies/past" = show all the movies that this customer has rented in the past, sorted by the checkout date. Include return date in response. + - From Rentals table find all of one customer's past rentals +SELECT title, checkout_date, return_date FROM movies, rentals WHERE rentals.customer_id = 2 AND rentals.returned_date != 'nil' ORDER BY checkout_date DESC;"; +Rental + +SELECT title, checkout_date, returned_date FROM movies, rentals where movies.id = rentals.movie_id and rentals.returned_date != 'nil' and rentals.customer_id = 4 ORDER BY checkout_date DESC; + +post "/rent/checkout" = Checks out the movie to the customer. Change the available inventory for that specific title. Do we decided how much to charge and when to return the movie? + * Add to Rentals table: + - customer_id :integer + - movie_id :integer + - checkout_date :string + - returned_date :string - nill when init + - rental_time :integer + - cost_per_day :integer + - total :integer - nil when init + +CURL =============================== +curl -X PUT -d '{"customer_id":"4", "movie_id":"5", "total": "5", "returned_date": "09-24-2015"}' -H 'contentype:application/json' "http://localhost:3000/rent/checkin" +===================================== +DB=development node ./utils/schema.js +DB=development node ./utils/seed.js +sqlite3 db/development.db + +πŸ”΅ put "/rent/checkin" = Checks in the movie to the customer. changes the available inventory for that specific title. + * Update Rentals table: + - returned_date :string + - total :integer + * Update Customers table; + - account_credit + * Update Movies table: + - inventory + - available + +http://localhost:3000/rent/overdue +πŸ”΅ get "/rent/overdue" = All customers with overdue movies. *Added extra feature: returns not just customers with overdue movies, but also overdue days* + * From Rentals table + - returned_date + - checkout_date + - rental_time + - if returned_date - checkout_date > rental_time || + - if returned_date == nil && if Time.now - checkout_date > rental_time + +*Added a new extra endpoint - rent history* +http://localhost:3000/rent +πŸ”΅ get "/rent" - all rental history diff --git a/package.json b/package.json new file mode 100644 index 0000000..a4ad257 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "C3Projects--VideoStoreAPI", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "nodemon ./bin/www", + "test": "DB=test mocha --recursive", + "test_schema": "DB=test node ./utils/run_schema.js", + "test_seed": "DB=test node ./utils/seed.js", + "db:schema": "node ./utils/run_schema.js", + "db:seed": "node ./utils/run_seed.js" + }, + "dependencies": { + "body-parser": "~1.13.2", + "cookie-parser": "~1.3.5", + "debug": "~2.2.0", + "express": "~4.13.1", + "jade": "~1.11.0", + "morgan": "~1.6.1", + "serve-favicon": "~2.3.0", + "sqlite3": "^3.1.0", + "supertest": "^1.1.0" + } +} diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css new file mode 100644 index 0000000..9453385 --- /dev/null +++ b/public/stylesheets/style.css @@ -0,0 +1,8 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} diff --git a/routes/customers.js b/routes/customers.js new file mode 100644 index 0000000..cc776da --- /dev/null +++ b/routes/customers.js @@ -0,0 +1,35 @@ +"use strict"; +var express = require('express'); +var router = express.Router(); +var customer_exports = require('../controllers/customers') + +// /* GET all customers */ +router.get('/', function(req, res, next) { + return customer_exports.customersController.findAllCustomers(req, res) +}) + +/* SORT Customers BY name alphabetically */ +router.get('/sort/name/:limit/:offset', function(req, res, next) { + return customer_exports.customersController.sortCustomersByName(req, res) +}); + +/* SORT Customers BY registered_at */ +router.get('/sort/registered_at/:limit/:offset', function(req, res, next) { + return customer_exports.customersController.sortCustomersByRegisteredAt(req, res) +}); + +/* SORT Customers BY postal_code */ +router.get('/sort/postal_code/:limit/:offset', function(req, res, next) { + return customer_exports.customersController.sortCustomersByPostalCode(req, res) +}); + +router.get('/:id/movies/past', function(req, res, next) { + customer_exports.customersController.getMoviesByCustomer(req, res) +}); + +router.get('/:id/movies/current', function(req, res, next) { + customer_exports.customersController.getCurrentMoviesbyCustomer(req, res); +}) + + +module.exports = router; diff --git a/routes/movies.js b/routes/movies.js new file mode 100644 index 0000000..c1f0109 --- /dev/null +++ b/routes/movies.js @@ -0,0 +1,50 @@ +"use strict"; +var express = require('express'); +var router = express.Router(); +var movies_exports = require('../controllers/movies') + +/* GET all movies */ +router.get('/', function(req, res, next) { + return movies_exports.moviesController.findAllMovies(req, res); +}); + +/* GET movie by title */ +router.get('/:title', function(req, res, next) { + return movies_exports.moviesController.findMovieByTitle(req, res); +}); + +/* SORT MOVIES BY TITLE alphabetically */ +router.get('/sort/title/:limit/:offset', function(req, res, next) { + return movies_exports.moviesController.sortMoviesByTitle(req, res) +}); + +/* SORT MOVIES BY release_date */ +router.get('/sort/release_date/:limit/:offset', function(req, res, next) { + return movies_exports.moviesController.sortMoviesByReleaseDate(req, res) +}); + + +router.get('/:title/customers/current', function(req, res, next) { + console.log(res); + return movies_exports.moviesController.currentCustomerRentals(req, res) +}); + +/* SHOWS INVENTORY OF AVAILABLE MOVIES */ +router.get('/:title/available', function(req, res, next) { + return movies_exports.moviesController.availableMovies(req, res) +}); + +router.get('/:title/customers/past/sort_by_id', function(req, res, next) { + movies_exports.moviesController.pastCustomerRentals(req, res) +}); + +router.get('/:title/customers/past/sort_by_name', function(req, res, next) { + movies_exports.moviesController.pastCustomerRentals(req, res) +}); + +router.get('/:title/customers/past/sort_by_checkout_date', function(req, res, next) { + movies_exports.moviesController.pastCustomerRentalsByDate(req, res) +}); + +module.exports = router; +// title?page=:page&number=:number diff --git a/routes/rentals.js b/routes/rentals.js new file mode 100644 index 0000000..c88ea53 --- /dev/null +++ b/routes/rentals.js @@ -0,0 +1,24 @@ +"use strict"; +var express = require('express'); +var router = express.Router(); +var rental_exports = require('../controllers/rentals') + +/* GET all customers rental history */ +router.get('/', function(req, res, next) { + return rental_exports.rentalsController.customersRentalHistory(req, res); +}); + +router.get('/overdue', function(req, res, next) { + return rental_exports.rentalsController.customersOverdue(req, res); +}); + +router.put('/checkin', function(req, res, next) { + return rental_exports.rentalsController.checkin(req, res); +}); + +router.post('/checkout', function(req, res, next) { + rental_exports.rentalsController.checkout(req, res); +}); + +module.exports = router; +// title?page=:page&number=:number diff --git a/test/controllers/customers_controller_test.js b/test/controllers/customers_controller_test.js new file mode 100644 index 0000000..62867b7 --- /dev/null +++ b/test/controllers/customers_controller_test.js @@ -0,0 +1,131 @@ +var request = require('supertest'), + assert = require('assert'), + app = require('../../app'), + sqlite3 = require('sqlite3').verbose(), + seeder = require('../../utils/seed'), + schema = require('../../utils/schema'), + agent = request.agent(app); + +describe("Endpoints under /customers", function() { + beforeEach(function(done) { + schema(done) + }) + + beforeEach(function(done) { + seeder(done) + }) + + describe("GET all customers", function() { + var customers_request; + + beforeEach(function(done) { + customers_request = agent.get('/customers').set('Accept', 'application/json'); + done(); + }) + + it("responds with json", function(done) { + customers_request + .expect('Content-Type', /application\/json/) + .expect(200, done); + }) + + it("returns an array", function(done) { + customers_request.expect(200, function(error, result) { + assert.equal(result.body.length, 7); //the db_cleaner inserted two records + + var keys = ['id', 'name', 'registered_at', 'address', 'city', 'state', 'postal_code', 'phone', 'account_credit']; + assert.deepEqual(Object.keys(result.body[0]), keys); + done(); + }) + }) + }) + + describe("GET a subset of customers", function() { + var customers_request; + + it("can get subset of customers in name order", function(done) { + customers_request = agent.get('/customers/sort/name/3/0').set('Accept', 'application/json'); + customers_request + .expect('Content-Type', /application\/json/) + .expect(200, function(error, result) { + assert.equal(result.body.length, 3); + + var expected_names = ['Aquila Riddle', 'Carolyn Chandler', 'Curran Stout'], + actual_names = []; + + for(var index in result.body) { + actual_names.push(result.body[index].name); + } + + assert.deepEqual(expected_names, actual_names); + done(error); + }) + }) + + it("can get a subset of customers in registered_at order", function(done){ + customers_request = agent.get('/customers/sort/registered_at/7/0').set('Accept', 'application/json'); + customers_request + .expect('Content-Type', /application\/json/) + .expect(200, function(error, result) { + assert.equal(result.body.length, 7); + + var expected_registered_at = ['Tue, 28 Jan 2014 22:28:45 -0800', 'Wed, 02 Apr 2014 21:44:46 -0700', 'Wed, 16 Apr 2014 21:40:20 -0700', 'Fri, 04 Jul 2014 11:05:11 -0700', 'Fri, 28 Nov 2014 13:14:08 -0800', 'Wed, 29 Apr 2015 07:54:14 -0700', 'Thu, 27 Aug 2015 08:17:24 -0700'], + actual_registered_at = []; + + for(var index in result.body) { + actual_registered_at.push(result.body[index].registered_at); + } + + assert.deepEqual(expected_registered_at, actual_registered_at); + + done(error); + }) + }) + + it("can get a subset of customers in postal_code order", function(done){ + customers_request = agent.get('/customers/sort/postal_code/3/0').set('Accept', 'application/json'); + customers_request + .expect('Content-Type', /application\/json/) + .expect(200, function(error, result) { + assert.equal(result.body.length, 3); + var expected_postal_code = ['15867', '24309', '36134'], + actual_postal_code = []; + + for(var index in result.body) { + actual_postal_code.push(result.body[index].postal_code); + } + + assert.deepEqual(expected_postal_code, actual_postal_code); + done(error); + }) + }) + }) + + describe("GET /customers/:id/movies/past", function() { + var customers_request; + + beforeEach(function(done) { + customers_request = agent.get('/customers/1/movies/past').set('Accept', 'application/json'); + done(); + }) + + it("responds with json", function(done) { + customers_request + .expect('Content-Type', /application\/json/) + .expect(200, done); + }) + + it("returns an array", function(done) { + customers_request.expect(200, function(error, result) { + assert.equal(result.body.length, 2); //the db_cleaner inserted two records + + var keys = ['title', 'checkout_date', 'returned_date']; + assert.deepEqual(Object.keys(result.body[0]), keys); + + assert.equal(result.body[0].title, 'The Exorcist'); + assert.equal(result.body[1].title, 'North by Northwest'); + done(); + }) + }) + }) +}) diff --git a/test/controllers/movies_controller_test.js b/test/controllers/movies_controller_test.js new file mode 100644 index 0000000..e3c865b --- /dev/null +++ b/test/controllers/movies_controller_test.js @@ -0,0 +1,230 @@ +var request = require('supertest'), + assert = require('assert'), + app = require('../../app'), + sqlite3 = require('sqlite3').verbose(), + seeder = require('../../utils/seed'), + schema = require('../../utils/schema'), + agent = request.agent(app); + +describe("Endpoints under /movies", function() { + describe("GET all movies", function() { + beforeEach(function(done) { + schema(done) + done(); + }); + + beforeEach(function(done) { + seeder(done) + done(); + }); + + var movie_request; + + beforeEach(function(done) { + movie_request = agent.get('/movies').set('Accept', 'application/json'); + done(); + }) + + it("responds with json", function(done) { + movie_request + .expect('Content-Type', /application\/json/) + .expect(200, done); + }) + + it("returns an array", function(done) { + movie_request.expect(200, function(error, result) { + assert.equal(result.body.length, 6); //the db_cleaner inserted two records + + var keys = ['id', 'title', 'overview', 'release_date', 'inventory', 'available']; + assert.deepEqual(Object.keys(result.body[0]), keys); + done(); + }) + }) + }) + + describe("GET a subset of movies", function() { + var movie_request; + + it("can get subset of movies in title order", function(done) { + + movie_request = agent.get('/movies/sort/title/4/1').set('Accept', 'application/json'); + movie_request + .expect('Content-Type', /application\/json/) + .expect(200, function(error, result) { + console.log(result.body); + assert.equal(result.body.length, 4); + + var expected_names = ['Jaws', 'North by Northwest', 'Psycho', 'The Exorcist'], + actual_names = []; + + for(var index in result.body) { + actual_names.push(result.body[index].title); + } + + assert.deepEqual(expected_names, actual_names); + done(error); + }) + }) + + it("can get a subset of movies in release_date order", function(done){ + movie_request = agent.get('/movies/sort/release_date/4/0').set('Accept', 'application/json'); + movie_request + .expect('Content-Type', /application\/json/) + .expect(200, function(error, result) { + assert.equal(result.body.length, 4); + var expected_release_date = ['1959-07-17', '1960-06-16', '1973-12-26', '1975-06-19'], + actual_release_date = []; + + for(var index in result.body) { + actual_release_date.push(result.body[index].release_date); + } + + assert.deepEqual(expected_release_date, actual_release_date); + + done(error); + }) + }) + }) + + describe("GET /movies/:title", function() { + var movie_request; + + beforeEach(function(done) { + movie_request = agent.get('/movies/Jaws').set('Accept', 'application/json'); + done(); + }) + + it("can find jaws", function(done) { + movie_request + .expect('Content-Type', /application\/json/) + .expect(200, function(error, result) { + assert.equal(result.body.length, 1); + + var keys = ['id', 'title', 'overview', 'release_date', 'inventory', 'available']; + assert.deepEqual(Object.keys(result.body[0]), keys); + + assert.equal(result.body[0].title, 'Jaws'); + done(); + }); + }) + }) + + describe("GET /movies/:title/available", function() { + var movie_request; + + beforeEach(function(done) { + movie_request = agent.get('/movies/Jaws/available').set('Accept', 'application/json'); + done(); + }) + + it("displays available quantity of jaws", function(done) { + movie_request + .expect('Content-Type', /application\/json/) + .expect(200, function(error, result) { + assert.equal(result.body.length, 1); + + var keys = ['title', 'available']; + assert.deepEqual(Object.keys(result.body[0]), keys); + + assert.equal(result.body[0].available, '6'); + done(); + }); + }) + }) + + describe("GET /movies/:title/customers/current", function() { + var movie_request; + + beforeEach(function(done) { + movie_request = agent.get('/movies/Jaws/customers/current').set('Accept', 'application/json'); + done(); + }) + + it("displays current customers of jaws", function(done) { + movie_request + .expect('Content-Type', /application\/json/) + .expect(200, function(error, result) { + assert.equal(result.body.length, 1); + + var keys = ['id', 'name', 'registered_at', 'address', 'city', 'state', 'postal_code', 'phone', 'account_credit']; + assert.deepEqual(Object.keys(result.body[0]), keys); + + assert.equal(result.body[0].name, 'Curran Stout'); + done(); + }); + }) + }) + + describe("GET /movies/:title/customers/past/sort_by_id", function() { + var movie_request; + + beforeEach(function(done) { + movie_request = agent.get('/movies/The Exorcist/customers/past/sort_by_id').set('Accept', 'application/json'); + done(); + }) + + it("displays past customers of the Exorcist, sorted by id", function(done) { + movie_request + .expect('Content-Type', /application\/json/) + .expect(200, function(error, result) { + assert.equal(result.body.length, 2); + + var keys = ['id', 'name', 'registered_at', 'address', 'city', 'state', 'postal_code', 'phone', 'account_credit']; + assert.deepEqual(Object.keys(result.body[0]), keys); + + assert.equal(result.body[0].name, 'Shelley Rocha'); + assert.equal(result.body[1].name, 'Carolyn Chandler'); + done(); + }); + }) + }) + + describe("GET /movies/:title/customers/past/sort_by_name", function() { + var movie_request; + + beforeEach(function(done) { + movie_request = agent.get('/movies/The Exorcist/customers/past/sort_by_name').set('Accept', 'application/json'); + done(); + }) + + it("displays past customers of the Exorcist, sorted by name", function(done) { + movie_request + .expect('Content-Type', /application\/json/) + .expect(200, function(error, result) { + assert.equal(result.body.length, 2); + + var keys = ['id', 'name', 'registered_at', 'address', 'city', 'state', 'postal_code', 'phone', 'account_credit']; + assert.deepEqual(Object.keys(result.body[0]), keys); + + assert.equal(result.body[0].name, 'Carolyn Chandler'); + assert.equal(result.body[1].name, 'Shelley Rocha'); + done(); + }); + }) + }) + + describe("GET /movies/:title/customers/past/sort_by_checkout_date", function() { + var movie_request; + + beforeEach(function(done) { + movie_request = agent.get('/movies/The Exorcist/customers/past/sort_by_checkout_date').set('Accept', 'application/json'); + done(); + }) + + it("displays past customers of the Exorcist, sorted by checkout_date", function(done) { + movie_request + .expect('Content-Type', /application\/json/) + .expect(200, function(error, result) { + assert.equal(result.body.length, 2); + + var keys = ['name', 'registered_at', 'address', 'city', 'state', 'postal_code', 'phone', 'account_credit', 'checkout_date']; + assert.deepEqual(Object.keys(result.body[0]), keys); + console.log(result.body) + + assert.equal(result.body[0].checkout_date, '09-22-2015'); + assert.equal(result.body[1].checkout_date, '09-20-2015'); + done(); + }); + }) + }) +}) diff --git a/test/controllers/rentals_controller_test.js b/test/controllers/rentals_controller_test.js new file mode 100644 index 0000000..bf5335f --- /dev/null +++ b/test/controllers/rentals_controller_test.js @@ -0,0 +1,26 @@ +var request = require('supertest'), + assert = require('assert'), + app = require('../../app'), + sqlite3 = require('sqlite3').verbose(), + agent = request.agent(app), + schema = require('../../utils/schema'), + seeder = require('../../utils/seed'); + +describe("rental controller", function() { + beforeEach(function(done) { + schema(done) + }) + + it("POST /rent/checkout", function(done) { + agent.post('/rent/checkout').set('Accept', 'application/json') + .send({'customer_id': 3}) + .send({'movie_id': 2}) + .send({'rental_time': 2}) + .send({'cost': 2.50}) + .expect(200, function(error, result) { + assert.equal(error, undefined) + assert.equal(result.body.lastID, 1); + done(); + }) + }) +}) diff --git a/test/models/customer_tests.js b/test/models/customer_tests.js new file mode 100644 index 0000000..0ba2b56 --- /dev/null +++ b/test/models/customer_tests.js @@ -0,0 +1,71 @@ +var assert = require("assert"), + Customer = require('../../models/customer'), + sqlite3 = require('sqlite3').verbose(), + seeder = require('../../utils/run_seed'), + schema = require('../../utils/run_schema') + + + +describe("Customer", function() { + beforeEach(function(){ + customer = new Customer(); + }) + + it("can be instantiated", function(){ + assert.equal(customer instanceof Customer, true); + }) + + it("has a 'find_all' property that is a function", function() { + assert.equal(typeof customer.find_all, "function"); + }) + + it("has a 'sort_by' property that is a function", function() { + assert.equal(typeof customer.sort_by, "function"); + }); + + describe("customer queries", function(){ + beforeEach(function(done){ + customer = new Customer(); + + db_cleaner = new sqlite3.Database('db/test.db'); + + db_cleaner.serialize(function(){ + db_cleaner.exec( + "BEGIN; \ + DELETE FROM customers; \ + INSERT INTO customers(name, registered_at, address, city, state, postal_code, phone, account_credit) \ + VALUES('Shelley Rocha', 'Wed, 29 Apr 2015 07:54:14 -0700', 'Ap #292-5216 Ipsum Rd.', 'Hillsboro', 'OR', '24309', '(322) 510-8695', 13.15), \ + ('Another Shelley Rocha', 'Wed, 30 Apr 2015 07:54:14 -0700', 'Ap #293-5216 Ipsum Rd.', 'Hillsboro', 'OR', '24310', '(322) 510-8700', 13.20); \ + COMMIT;", + function(err) { + db_cleaner.close(); + done(); + } + ); + }) + }) + + it("finds 'Shelly Rocha' name in the customers table", function(done) { + customer.find_by('name', "Shelley Rocha", function(err, result){ + assert.equal(result[0].name, 'Shelley Rocha'); + done(); + }); + }) + + it("displays all records from customers table", function(done) { + customer.find_all(function(err, result) { + assert.equal(result.length, 2); + done(); + }); + }) + + it("displays all records from 'customers' table, sorted by name with limit 2, offset 0", function(done) { + customer.sort_by("name", 2, 0, function(err, result) { + assert.equal(result[0].name, 'Another Shelley Rocha'); + assert.equal(result[1].name, 'Shelley Rocha'); + assert.equal(result.length, 2); + done(); + }); + }) + }) +}); diff --git a/test/models/movie_tests.js b/test/models/movie_tests.js new file mode 100644 index 0000000..85bbe88 --- /dev/null +++ b/test/models/movie_tests.js @@ -0,0 +1,83 @@ +var assert = require("assert"), + Movie = require('../../models/movie'), + sqlite3 = require('sqlite3').verbose(); + + +describe("Movie", function() { + beforeEach(function(){ + movie = new Movie(); + }) + + it("can be instantiated", function(){ + assert.equal(movie instanceof Movie, true); + }) + + it("has a find_by property that is a function", function() { + assert.equal(typeof movie.find_by, "function"); + }) + + it("has a 'find_all' property that is a function", function() { + assert.equal(typeof movie.find_all, "function"); + }) + + it("has a 'sort_by' property that is a function", function() { + assert.equal(typeof movie.sort_by, "function"); + }); + + describe("movie queries", function(){ + beforeEach(function(done){ + movie = new Movie(); + + db_cleaner = new sqlite3.Database('db/test.db'); + + db_cleaner.serialize(function(){ + db_cleaner.exec( + "BEGIN; \ + DELETE FROM movies; \ + INSERT INTO movies(title, overview, release_date, inventory, available) \ + VALUES('Jaws', 'Shark!', 'Yesterday', 10, 10), \ + ('Aws', 'Shark!', 'Yesterday', 10, 10), \ + ('Waws', 'Worm!', 'Yesterday', 11, 0), \ + ('Maws', 'Worm!', 'Yesterday', 11, 11); \ + COMMIT;", + function(err) { + db_cleaner.close(); + done(); + } + ); + }) + }) + + it("finds 'Jaws' title in the movies table", function(done) { + movie.find_by('title', "Jaws", function(err, result){ + assert.equal(result[0].title, 'Jaws'); + done(); + }); + + }); + + it("displays all records from movies table", function(done) { + movie.find_all(function(err, result) { + assert.equal(result.length, 4); + done(); + }); + }) + + it("displays all records from 'movies' table, sorted by title with limit 2", function(done) { + movie.sort_by("title", 2, 0, function(err, result) { + assert.equal(result[0].title, 'Aws'); + assert.equal(result[1].title, 'Jaws'); + assert.equal(result.length, 2); + done(); + }); + }) + + it("displays 10 available 'Jaws' movie", function(done) { + movie.available("Jaws", function(err, result) { + assert.equal(result[0].title, 'Jaws'); + assert.equal(result[0].available, 10); + done(); + }); + }) + }) +}); diff --git a/test/models/rentals_tests.js b/test/models/rentals_tests.js new file mode 100644 index 0000000..ec187bd --- /dev/null +++ b/test/models/rentals_tests.js @@ -0,0 +1,56 @@ +var assert = require("assert"), + Rental = require('../../models/rental'), + sqlite3 = require('sqlite3').verbose(); + + +describe("Rental", function() { + beforeEach(function(){ + rental = new Rental(); + }) + + it("can be instantiated", function(){ + assert.equal(rental instanceof Rental, true); + }) + + it("has a 'customersRentalHistory' property that is a function", function() { + assert.equal(typeof rental.customersRentalHistory, "function"); + }) + + describe("rentals queries", function(){ + beforeEach(function(done){ + rental = new Rental(); + + db_cleaner = new sqlite3.Database('db/test.db'); + + db_cleaner.serialize(function(){ + db_cleaner.exec( + "BEGIN; \ + DELETE FROM rentals; \ + INSERT INTO rentals(checkout_date, returned_date, rental_time, cost, total, customer_id, movie_id) \ + VALUES('09-22-2015', '09-25-2015', '3', '2.50', '5', '1', '1'), \ + ('09-23-2015', '09-26-2015', '3', '2.50', '5', '2', '2'), \ + ('09-24-2015', '09-30-2015', '6', '2.50', '5', '1', '3'), \ + ('09-25-2015', '09-25-2015', '1', '2.50', '5', '2', '4'), \ + ('09-19-2015', 'nil', '6', '2.50', null, '1', '6'), \ + ('09-18-2015', 'nil', '1', '2.50', null, '2', '5'); \ + COMMIT;", + function(err) { + db_cleaner.close(); + done(); + } + ); + }) + }) + + it("displays all customers rental history", function(done) { + rental.customersRentalHistory(function(err, result){ + assert.equal(result[0].name, 'Another Shelley Rocha'); + assert.equal(result[0].checkout_date, '09-25-2015'); + assert.equal(result[0].returned_date, '09-25-2015'); + assert.equal(result[0].rental_time, 1); + assert.equal(result.length, 6); + done(); + }); + }); + }) +}); diff --git a/customers.json b/utils/customers-development.json similarity index 100% rename from customers.json rename to utils/customers-development.json diff --git a/utils/customers-test.json b/utils/customers-test.json new file mode 100644 index 0000000..b428ac0 --- /dev/null +++ b/utils/customers-test.json @@ -0,0 +1,79 @@ +[ + { + "id": "1", + "name": "Shelley Rocha", + "registered_at": "Wed, 29 Apr 2015 07:54:14 -0700", + "address": "Ap #292-5216 Ipsum Rd.", + "city": "Hillsboro", + "state": "OR", + "postal_code": "24309", + "phone": "(322) 510-8695", + "account_credit": 13.15 + }, + { + "id": "2", + "name": "Curran Stout", + "registered_at": "Wed, 16 Apr 2014 21:40:20 -0700", + "address": "Ap #658-1540 Erat Rd.", + "city": "San Francisco", + "state": "California", + "postal_code": "94267", + "phone": "(908) 949-6758", + "account_credit": 35.66 + }, + { + "id": "3", + "name": "Roanna Robinson", + "registered_at": "Fri, 28 Nov 2014 13:14:08 -0800", + "address": "Ap #561-4214 Eget St.", + "city": "Harrisburg", + "state": "PA", + "postal_code": "15867", + "phone": "(323) 336-1841", + "account_credit": 50.39 + }, + { + "id": "4", + "name": "Carolyn Chandler", + "registered_at": "Fri, 04 Jul 2014 11:05:11 -0700", + "address": "133-8707 Arcu. Avenue", + "city": "Fort Wayne", + "state": "IN", + "postal_code": "73684", + "phone": "(234) 837-2886", + "account_credit": 21.79 + }, + { + "id": "5", + "name": "Aquila Riddle", + "registered_at": "Thu, 27 Aug 2015 08:17:24 -0700", + "address": "Ap #187-9582 Primis St.", + "city": "Tacoma", + "state": "WA", + "postal_code": "73251", + "phone": "(925) 161-2223", + "account_credit": 17.82 + }, + { + "id": "6", + "name": "Phyllis Russell", + "registered_at": "Wed, 02 Apr 2014 21:44:46 -0700", + "address": "746-8511 Ipsum Ave", + "city": "Boise", + "state": "Idaho", + "postal_code": "76759", + "phone": "(961) 964-5158", + "account_credit": 88.67 + }, + { + "id": "7", + "name": "Rajah Riggs", + "registered_at": "Tue, 28 Jan 2014 22:28:45 -0800", + "address": "Ap #881-3920 Malesuada Avenue", + "city": "Norman", + "state": "OK", + "postal_code": "36134", + "phone": "(540) 515-2339", + "account_credit": 30.14 + } +] diff --git a/movies.json b/utils/movies-development.json similarity index 100% rename from movies.json rename to utils/movies-development.json diff --git a/utils/movies-test.json b/utils/movies-test.json new file mode 100644 index 0000000..ddc243b --- /dev/null +++ b/utils/movies-test.json @@ -0,0 +1,38 @@ +[ + { + "title": "Psycho", + "overview": "When larcenous real estate clerk Marion Crane goes on the lam with a wad of cash and hopes of starting a new life, she ends up at the notorious Bates Motel, where manager Norman Bates cares for his housebound mother. The place seems quirky, but fine… until Marion decides to take a shower.", + "release_date": "1960-06-16", + "inventory": 8 + }, + { + "title": "Jaws", + "overview": "An insatiable great white shark terrorizes the townspeople of Amity Island, The police chief, an oceanographer and a grizzled shark hunter seek to destroy the bloodthirsty beast.", + "release_date": "1975-06-19", + "inventory": 6 + }, + { + "title": "The Exorcist", + "overview": "12-year-old Regan MacNeil begins to adapt an explicit new personality as strange events befall the local area of Georgetown. Her mother becomes torn between science and superstition in a desperate bid to save her daughter, and ultimately turns to her last hope: Father Damien Karras, a troubled priest who is struggling with his own faith.", + "release_date": "1973-12-26", + "inventory": 7 + }, + { + "title": "North by Northwest", + "overview": "Madison Avenue advertising man Roger Thornhill finds himself thrust into the world of spies when he is mistaken for a man by the name of George Kaplan. Foreign spy Philip Vandamm and his henchman Leonard try to eliminate him but when Thornhill tries to make sense of the case, he is framed for murder. Now on the run from the police, he manages to board the 20th Century Limited bound for Chicago where he meets a beautiful blond, Eve Kendall, who helps him to evade the authorities. His world is turned upside down yet again when he learns that Eve isn't the innocent bystander he thought she was. Not all is as it seems however, leading to a dramatic rescue and escape at the top of Mt. Rushmore.", + "release_date": "1959-07-17", + "inventory": 10 + }, + { + "title": "The Silence of the Lambs", + "overview": "FBI trainee Clarice Starling ventures into a maximum-security asylum to pick the diseased brain of Hannibal Lecter, a psychiatrist turned homicidal cannibal. Starling needs clues to help her capture a serial killer. Unfortunately, her Faustian relationship with Lecter soon leads to his escape, and now two deranged killers are on the loose.", + "release_date": "1991-02-14", + "inventory": 3 + }, + { + "title": "Alien", + "overview": "During its return to the earth, commercial spaceship Nostromo intercepts a distress signal from a distant planet. When a three-member team of the crew discovers a chamber containing thousands of eggs on the planet, a creature inside one of the eggs attacks an explorer. The entire crew is unaware of the impending nightmare set to descend upon them when the alien parasite planted inside its unfortunate host is birthed.", + "release_date": "1979-05-25", + "inventory": 4 + } +] diff --git a/utils/rentals-development.json b/utils/rentals-development.json new file mode 100644 index 0000000..c12ca80 --- /dev/null +++ b/utils/rentals-development.json @@ -0,0 +1,61 @@ +[ + { + "checkout_date": "09-22-2015", + "returned_date": "09-23-2015", + "rental_time": "1", + "cost": "2.50", + "total": "2.50", + "customer_id": "1", + "movie_id": "3" + }, + + { + "checkout_date": "09-22-2015", + "returned_date": "09-24-2015", + "rental_time": "2", + "cost": "2.50", + "total": "5", + "customer_id": "1", + "movie_id": "4" + }, + + { + "checkout_date": "09-18-2015", + "returned_date": "nil", + "rental_time": "3", + "cost": "2.50", + "total": null, + "customer_id": "2", + "movie_id": "2" + }, + + { + "checkout_date": "09-20-2015", + "returned_date": "nil", + "rental_time": "2", + "cost": "2.50", + "total": null, + "customer_id": "4", + "movie_id": "5" + }, + + { + "checkout_date": "09-20-2015", + "returned_date": "09-23-2015", + "rental_time": "3", + "cost": "2.50", + "total": "7.50", + "customer_id": "4", + "movie_id": "3" + }, + + { + "checkout_date": "09-20-2015", + "returned_date": "nil", + "rental_time": "2", + "cost": "2.50", + "total": "nil", + "customer_id": "3", + "movie_id": "5" + } +] diff --git a/utils/rentals-test.json b/utils/rentals-test.json new file mode 100644 index 0000000..4e54aef --- /dev/null +++ b/utils/rentals-test.json @@ -0,0 +1,61 @@ +[ + { + "checkout_date": "09-22-2015", + "returned_date": "09-23-2015", + "rental_time": "1", + "cost": "2.50", + "total": "2.50", + "customer_id": "1", + "movie_id": "3" + }, + + { + "checkout_date": "09-22-2015", + "returned_date": "09-24-2015", + "rental_time": "2", + "cost": "2.50", + "total": "5", + "customer_id": "1", + "movie_id": "4" + }, + + { + "checkout_date": "09-18-2015", + "returned_date": "nil", + "rental_time": "3", + "cost": "2.50", + "total": "nil", + "customer_id": "2", + "movie_id": "2" + }, + + { + "checkout_date": "09-20-2015", + "returned_date": "nil", + "rental_time": "2", + "cost": "2.50", + "total": "nil", + "customer_id": "4", + "movie_id": "5" + }, + + { + "checkout_date": "09-20-2015", + "returned_date": "09-23-2015", + "rental_time": "3", + "cost": "2.50", + "total": "7.50", + "customer_id": "4", + "movie_id": "3" + }, + + { + "checkout_date": "09-20-2015", + "returned_date": "nil", + "rental_time": "2", + "cost": "2.50", + "total": "nil", + "customer_id": "3", + "movie_id": "5" + } +] diff --git a/utils/run_schema.js b/utils/run_schema.js new file mode 100644 index 0000000..1b4b84c --- /dev/null +++ b/utils/run_schema.js @@ -0,0 +1,7 @@ +// "use strict"; +// var schema = require('./schema'); +// +// schema(function(error, result) { +// console.log(error); +// console.log("im running the schema"); +// }) diff --git a/utils/run_seed.js b/utils/run_seed.js new file mode 100644 index 0000000..6d96001 --- /dev/null +++ b/utils/run_seed.js @@ -0,0 +1,10 @@ + +"use strict"; + +var seeder = require('./seed'); + +seeder(function(error, result) { + console.log(error); + console.log("im running the seeder"); +}) + diff --git a/utils/schema.js b/utils/schema.js new file mode 100644 index 0000000..60464fc --- /dev/null +++ b/utils/schema.js @@ -0,0 +1,88 @@ +"use strict"; + +module.exports = function(callback) { + var sqlite3 = require("sqlite3").verbose(), + db_env = process.env.DB || 'development', + db = new sqlite3.Database('db/' + db_env + '.db'); + + var movie_fields = [ + ['title', 'text'], + ['overview', 'text'], + ['release_date', 'text'], + ['inventory', 'integer'], + ['available', 'integer'] + ]; + + var customer_fields = [ + ['name', 'text'], + ['registered_at', 'text'], + ['address', 'text'], + ['city', 'text'], + ['state', 'text'], + ['postal_code', 'text'], + ['phone', 'text'], + ['account_credit', 'integer'] + ]; + + var rental_fields = [ + ['checkout_date', 'text'], + ['returned_date', 'text'], + ['rental_time', 'integer'], + ['cost', 'integer'], + ['total', 'integer'], + ['customer_id', 'integer', 'fk_CustomerRentals FOREIGN KEY(customer_id) REFERENCES customers(id)'], + ['movie_id', 'integer', 'fk_MovieRentals FOREIGN KEY(movie_id) REFERENCES movies(id)'] + // ["FOREIGN KEY(customer_id)", "REFERENCES customers(id)"], + // ["FOREIGN KEY(movie_id)", "REFERENCES movies(id)"] + ]; + + db.serialize(function() { + db.exec("BEGIN IMMEDIATE"); + + db.exec("DROP TABLE IF EXISTS customers;"); + + db.exec("CREATE TABLE customers (id INTEGER PRIMARY KEY);"); + + for(var i = 0; i < customer_fields.length; i++){ + var name = customer_fields[i][0], + type = customer_fields[i][1]; + db.exec("ALTER TABLE customers ADD COLUMN " + name + " " + type + ";"); + } + + db.exec("DROP TABLE IF EXISTS movies;"); + + db.exec("CREATE TABLE movies (id INTEGER PRIMARY KEY);"); + + for(var i = 0; i < movie_fields.length; i++){ + var name = movie_fields[i][0], + type = movie_fields[i][1]; + db.exec("ALTER TABLE movies ADD COLUMN " + name + " " + type + ";", function(error, result){ + }); + } + + db.exec("DROP TABLE IF EXISTS rentals;"); + + db.exec("CREATE TABLE rentals (id INTEGER PRIMARY KEY);"); + + for(var i = 0; i < rental_fields.length; i++){ + var name = rental_fields[i][0], + type = rental_fields[i][1]; + if(rental_fields[i][2] == void 0){ + var foreign_key = rental_fields[i][2]; + } + + db.exec("ALTER TABLE rentals ADD COLUMN " + name + " " + type + ";"); + if(foreign_key) { + db.exec("ALTER TABLE rentals ADD CONSTRAINT " + foreign_key + ";"); + } + } + db.exec("COMMIT", function(error) { + console.log("I'm done setting up the db") + callback(error, "Success"); + db.close(); + }); + }); + + // db.close(); + // callback() +} diff --git a/utils/seed.js b/utils/seed.js new file mode 100644 index 0000000..be2cfb9 --- /dev/null +++ b/utils/seed.js @@ -0,0 +1,75 @@ +"use strict"; + +module.exports = function(callback) { + var sqlite3 = require("sqlite3").verbose(), + db_env = process.env.DB || 'development', + db = new sqlite3.Database('db/' + db_env + '.db'); + + // MOVIES TABLE ========================================================================= + var movies = require('./movies-' + db_env + '.json'); + + // CUSTOMERS TABLE ========================================================================= + var customers = require('./customers-' + db_env + '.json'); + + // RENTAL TABLE =============================================================================== + var rentals = require('./rentals-' + db_env + '.json'); + + db.serialize(function() { + db.exec("BEGIN IMMEDIATE"); + var rental_statement = db.prepare( + "INSERT INTO rentals(checkout_date, returned_date, rental_time, cost, total, customer_id, movie_id) VALUES (?, ?, ?, ?, ?, ?, ?);" + ); + for(var i = 0; i < rentals.length; i++) { + var rental = rentals[i]; + rental_statement.run( + rental.checkout_date, + rental.returned_date, + rental.rental_time, + rental.cost, + rental.total, + rental.customer_id, + rental.movie_id + ); + } + rental_statement.finalize(); + + var movie_statement = db.prepare( + "INSERT INTO movies(title, overview, inventory, release_date, available) VALUES (?, ?, ?, ?, ?);" + ); + for(var i= 0; i < movies.length; i ++) { + var movie = movies[i]; + movie_statement.run( + movie.title, + movie.overview, + movie.inventory, + movie.release_date, + movie.inventory + ); + } + movie_statement.finalize(); + + var customer_statement = db.prepare( + "INSERT INTO customers(name, registered_at, address, city, state, postal_code, phone, account_credit) VALUES (?, ?, ?, ?, ?, ?, ?, ?);" + ); + + for(var i= 0; i < customers.length; i ++) { + var customer = customers[i]; + customer_statement.run( + customer.name, + customer.registered_at, + customer.address, + customer.city, + customer.state, + customer.postal_code, + customer.phone, + customer.account_credit + ); + } + customer_statement.finalize(); + + db.exec("COMMIT", function(error) { + callback(error, "I'm done seeding the db"); + db.close(); + }); + }) +} diff --git a/views/error.jade b/views/error.jade new file mode 100644 index 0000000..51ec12c --- /dev/null +++ b/views/error.jade @@ -0,0 +1,6 @@ +extends layout + +block content + h1= message + h2= error.status + pre #{error.stack} diff --git a/views/index.jade b/views/index.jade new file mode 100644 index 0000000..3d63b9a --- /dev/null +++ b/views/index.jade @@ -0,0 +1,5 @@ +extends layout + +block content + h1= title + p Welcome to #{title} diff --git a/views/layout.jade b/views/layout.jade new file mode 100644 index 0000000..15af079 --- /dev/null +++ b/views/layout.jade @@ -0,0 +1,7 @@ +doctype html +html + head + title= title + link(rel='stylesheet', href='/stylesheets/style.css') + body + block content