Problem: C$50 Finance (Part 2)

Prerequisites

  • PHP syntax

  • PHP web programming

  • C$50 Finance (Part 1)

Objectives

  • Introduce you to HTML, CSS, PHP, and SQL.

  • Teach you how to teach yourself new languages.

This problem is an extension of Part 1. As such, you should have already watched all the videos necessary to finishing C$50 Finance. If stuck, feel free to take a peek back at the videos, which can be found in the specification for Part 1!

Academic Honesty

This course’s philosophy on academic honesty is best stated as "be reasonable." The course recognizes that interactions with classmates and others can facilitate mastery of the course’s material. However, there remains a line between enlisting the help of another and submitting the work of another. This policy characterizes both sides of that line.

The essence of all work that you submit to this course must be your own. Collaboration on problem sets is not permitted except to the extent that you may ask classmates and others for help so long as that help does not reduce to another doing your work for you. Generally speaking, when asking for help, you may show your code to others, but you may not view theirs, so long as you and they respect this policy’s other constraints. Collaboration on the course’s test and quiz is not permitted at all. Collaboration on the course’s final project is permitted to the extent prescribed by its specification.

Below are rules of thumb that (inexhaustively) characterize acts that the course considers reasonable and not reasonable. If in doubt as to whether some act is reasonable, do not commit it until you solicit and receive approval in writing from the course’s heads. Acts considered not reasonable by the course are handled harshly. If the course refers some matter for disciplinary action and the outcome is punitive, the course reserves the right to impose local sanctions on top of that outcome that may include an unsatisfactory or failing grade for work submitted or for the course itself. The course ordinarily recommends exclusion (i.e., required withdrawal) from the course itself.

If you commit some act that is not reasonable but bring it to the attention of the course’s heads within 72 hours, the course may impose local sanctions that may include an unsatisfactory or failing grade for work submitted, but the course will not refer the matter for further disciplinary action except in cases of repeated acts.

Reasonable

  • Communicating with classmates about problem sets' problems in English (or some other spoken language).

  • Discussing the course’s material with others in order to understand it better.

  • Helping a classmate identify a bug in his or her code at office hours, elsewhere, or even online, as by viewing, compiling, or running his or her code, even on your own computer.

  • Incorporating a few lines of code that you find online or elsewhere into your own code, provided that those lines are not themselves solutions to assigned problems and that you cite the lines' origins.

  • Reviewing past semesters' quizzes and solutions thereto.

  • Sending or showing code that you’ve written to someone, possibly a classmate, so that he or she might help you identify and fix a bug.

  • Sharing a few lines of your own code online so that others might help you identify and fix a bug.

  • Turning to the course’s heads for help or receiving help from the course’s heads during the quiz or test.

  • Turning to the web or elsewhere for instruction beyond the course’s own, for references, and for solutions to technical difficulties, but not for outright solutions to problem set’s problems or your own final project.

  • Whiteboarding solutions to problem sets with others using diagrams or pseudocode but not actual code.

  • Working with (and even paying) a tutor to help you with the course, provided the tutor does not do your work for you.

Not Reasonable

  • Accessing a solution to some problem prior to (re-)submitting your own.

  • Asking a classmate to see his or her solution to a problem set’s problem before (re-)submitting your own.

  • Decompiling, deobfuscating, or disassembling the staff’s solutions to problem sets.

  • Failing to cite (as with comments) the origins of code or techniques that you discover outside of the course’s own lessons and integrate into your own work, even while respecting this policy’s other constraints.

  • Giving or showing to a classmate a solution to a problem set’s problem when it is he or she, and not you, who is struggling to solve it.

  • Looking at another individual’s work during the test or quiz.

  • Paying or offering to pay an individual for work that you may submit as (part of) your own.

  • Providing or making available solutions to problem sets to individuals who might take this course in the future.

  • Searching for or soliciting outright solutions to problem sets online or elsewhere.

  • Splitting a problem set’s workload with another individual and combining your work.

  • Submitting (after possibly modifying) the work of another individual beyond the few lines allowed herein.

  • Submitting the same or similar work to this course that you have submitted or will submit to another.

  • Submitting work to this course that you intend to use outside of the course (e.g., for a job) without prior approval from the course’s heads.

  • Turning to humans (besides the course’s heads) for help or receiving help from humans (besides the course’s heads) during the quiz or test.

  • Viewing another’s solution to a problem set’s problem and basing your own solution on it.

Assessment

Your work on this problem set will be evaluated along four axes primarily.

Scope

To what extent does your code implement the features required by our specification?

Correctness

To what extent is your code consistent with our specifications and free of bugs?

Design

To what extent is your code written well (i.e., clearly, efficiently, elegantly, and/or logically)?

Style

To what extent is your code readable (i.e., commented and indented with variables aptly named)?

All students, whether or not taking the course for a letter grade, must ordinarily submit this and all other problem sets to be eligible for a satisfactory grade unless granted an exception in writing by the course’s heads.

Set It Up

You have two options in setting up this problem:

  1. Use your own code from Part 1 and simply extend it, in which case you can skip down to the section entitled Money Never Sleeps

  2. Download the staff solution from Part 1 and extend that, the instructions for which are below

Log into CS50 IDE and, in a terminal window, execute

update50
sudo apt-get install -y php5-xdebug

to ensure that your workspace is up-to-date!

Like Part 1, this problem set comes with some distribution code that you’ll need to download before getting started. Go ahead and execute

cd ~/workspace

in order to navigate to your ~/workspace directory. Then execute

wget http://docs.cs50.net/2016/ap/problems/finance/2/finance2.zip

in order to download a ZIP (i.e., compressed version) of this problem set’s distro as well as a MySQL database (that’s been exported to a text file). If you then execute

ls

you should see that you now have a file called finance2.zip in your ~/workspace directory (along with finance2.sql). Unzip it by executing the below.

unzip finance2.zip

If you again execute

ls

you should see that you now also have a directory called 8-3. You’re now welcome to delete the ZIP file with the below.

rm -f 8-3.zip

If you next execute

cd 8-3

followed by

ls

you should see that 8-3 contains:

config.json  includes/  public/  vendor/  views/

But more on those soon.

Next, ensure that ~/workspace/8-3/public is "world-executable" by executing

chmod a+x ~/workspace/8-3/public

so that CS50 IDE’s web server (Apache) and you (from a browser) will be able to access your work. Then, navigate your way to ~/workspace/8-3/public by executing the below.

cd ~/workspace/8-3/public

If you execute

ls

you should see that public contains four subdirectories and three files. Ensure that the former are world-executable by executing the below.

chmod a+x css fonts img js

And ensure that the files within those directories are world-readable by executing the below.

chmod a+r css/* fonts/* img/* js/*

If unfamiliar, * is a "wildcard character," so css/*, for instance, simply means "all files within the css directory."

For security’s sake, don’t make ~/workspace/8-3/includes or ~/workspace/8-3/views world-executable (or their contents world-readable), as they shouldn’t be (potentially) accessible to the whole world (only to your PHP code, as you’ll soon see).

Okay, let’s now configure CS50 IDE’s web server (aka Apache) to use ~/workspace/8-3/public as its root. First ensure that Problem Set 6’s server isn’t still running (e.g., in another tab) by executing the below.

killall -9 server

Next, ensure that Apache isn’t already running (with some other root) by executing the below.

apache50 stop

Then (re)start Apache with the below so that it uses ~/workspace/8-3/public as its root.

apache50 start ~/workspace/8-3/public

Next, start CS50 IDE’s database server (MySQL) by executing the below.

mysql50 start

Then open up 8-3/config.json, which is a configuration file in JSON (JavaScript Object Notation) format, which essentially means it’s a collection of key-value pairs. JSON is a popular format for configuration files these days, since libraries that can read (i.e., parse) it exist for lots of languages, PHP among them. The curly braces at the top and bottom of this file indicate that the file contains an object, inside of which is one key (database) whose value is another object (per the innermost curly braces). That latter object, meanwhile, has four keys (host, name, password, and username), each of whose values is a string, two of which are TODO! Those values will be used by CS50’s PHP library (which can be found in 8-3/vendor) in order to connect to your workspace’s MySQL database. CS50’s PHP library includes a function, query, that will allow you to send queries to that database.

Anyhow, let’s tackle those TODOs! Click the ⓘ icon toward CS50 IDE’s top-right corner. You should see your MySQL Username and your MySQL Password. Copy and paste those values, one at a time, into the appropriate places in config.json, then save and close that file.

It’s now time for a test! Visit https://ide50-username.cs50.io/, where username is your own username. You should find yourself redirected to C$50 Finance’s login page! (If you instead see Forbidden, odds are you missed a step earlier; best to try all those chmod steps again.) If you try logging into C$50 Finance with a username of, oh, skroob and a password of 12345, you should encounter an error about an Unknown database. That’s simply because you haven’t created it yet! Let’s create it.

In a separate tab, head to https://ide50-username.cs50.io/phpmyadmin (where username is, again, your own username) to access phpMyAdmin, a Web-based tool (that happens to be written in PHP) with which you can manage MySQL databases. (MySQL is a free, open-source database that CS50, Facebook, and lots of other sites use.) Log in with the same username and password that you pasted into config.json. You should then find yourself at phpMyAdmin’s main page.

Within CS50 IDE, now, open up 8-3.sql, which you downloaded earlier (via wget). You should see a whole bunch of SQL statements. Highlight them all, select Edit > Copy (or hit control-c), then return to phpMyAdmin. Click phpMyAdmin’s SQL tab, and paste everything you copied into that page’s big text box (which is below Run SQL query/queries on server "127.0.0.1"). Skim what you just pasted to get a sense of the commands you’re about to execute, then click Go. You should then see a greenish banner indicating success (i.e., 1 row affected). In phpMyAdmin’s top-left corner, you should now see link to a database called 8-3, beneath which is a link to a table called users. (If you don’t, try reloading the page.) But more on those later.

Return to https://ide50-username.cs50.io/ and reload that page. Then try to log in again with a username of skroob and a password of 12345. 0:-)

chmod

Just like Problem 8-3, you’ll have to chmod any new PHP files you write. If you remember how, great! If you don’t, just look back at the chmod section of the Problem 8-3 spec!

Money Never Sleeps

In Part 1, you implemented the functionality to register new users, get stock quotes, and show a user’s stock portfolio. Now, its time to let them "buy" and "sell" stocks, filling up (or emptying) their portfolio!

sell

And now it is time to implement the ability to sell with a controller called, say, sell.php and some number of views. We leave the design of this feature to you. But know that you can delete rows from your table (on behalf of, say, President Skroob) with SQL like the below.

DELETE FROM portfolios WHERE user_id = 9 AND symbol = 'FREE'

We leave it to you to infer exactly what that statement should do. Of course, you could try the above out via phpMyAdmin’s SQL tab. Now what about the user’s cash balance? Odds are, your user is going to want the proceeds of all sales. So selling a stock involves updating not only your table for users' portfolios but users as well. We leave it to you to determine how to compute how much cash a user is owed upon sale of some stock. But once you know that amount (say, $500), SQL like the below should take care of the deposit (for, say, President Skroob).

UPDATE users SET cash = cash + 500 WHERE id = 9

Of course, if the database or web server happens to die between this DELETE and UPDATE, President Skroob might lose out on all of that cash. You need not worry about such cases! It’s also possible, because of multithreading and, thus, race conditions, that a clever president could trick your site into paying out more than once. You need not worry about such cases either! Though, if you’re so very inclined, you can employ SQL "transactions" (with InnoDB tables). See http://dev.mysql.com/doc/refman/5.5/en/sql-syntax-transactions.html if curious.

It’s fine, for simplicity, to require that users sell all shares of some stock or none, rather than only a few. Needless to say, try out your code by logging in as some user and selling some stuff. You can always "buy" it back manually with phpMyAdmin.

As always, be sure that your HTML is valid!

And as always, here is Zamyla!

buy

Now it’s time to support actual buys. Implement the ability to buy, with a controller called, say, buy.php and some number of views. (As before, you need not worry about interruptions of service or race conditions.) The interface with which you provide a user is entirely up to you, though, as before, feel free to look to https://finance.cs50.net/ for inspiration or hints. Of course, you’ll need to ensure that a user cannot spend more cash than he or she has on hand. And you’ll want to make sure that users can only buy whole shares of stocks, not fractions thereof. For this latter requirement, know that a call like

preg_match("/^\d+$/", $_POST["shares"])

will return true if and only if $_POST["shares"] contains a non-negative integer, thanks to its use of a regular expression. See http://www.php.net/preg_match for details. Take care to apologize to the user if you must reject their input for any reason. In other words, be sure to perform rigorous error-checking. (We leave to you to determine what needs to be checked!)

When it comes time to store stocks' symbols in your database table, take care to store them in uppercase (as is convention), no matter how they were inputted by users, so that you don’t accidentally treat, say, free and FREE as different stocks. Don’t force users, though, to input symbols in uppercase.

Incidentally, if you implemented your table for users' portfolios as we did ours (with that joint key), know that SQL like the below (which, unfortunately, wraps onto two lines) will insert a new row into table unless the specified pair of id and symbol already exists in some row, in which case that row’s number of shares will simply be increased (say, by 10).

INSERT INTO portfolios (user_id, symbol, shares) VALUES(9, 'FREE', 10) ON DUPLICATE KEY UPDATE shares = shares + VALUES(shares)

As always, be sure to bang on your code. And be sure that your HTML is valid!

Here’s Zamyla with some additional help:

history

Alright, so your users can now buy and sell stocks and even check their portfolio’s value. But they have no way of viewing their history of transactions.

Enhance your implementations for buying and selling in such a way that you start logging transactions, recording for each:

  • Whether a stock was bought or sold.

  • The symbol bought or sold.

  • The number of shares bought or sold.

  • The price of a share at the time of transaction.

  • The date and time of the transaction.

Then, by way of a controller called, say, history.php and some number of views, enable users to peruse their own history of transactions, formatted as you see fit. Be sure that your HTML is valid! Feel free to learn from the staff’s implementation of C$50 Finance at https://finance.cs50.net/.

Here’s Zamyla again:

Check It Out

Before you consider this problem set done, best to ask yourself these questions and then go back and improve your code as needed! Do not consider the below an exhaustive list of expectations, though, just some helpful reminders. The checkboxes that have come before these represent the exhaustive list! To be clear, consider the questions below rhetorical. No need to answer them in writing for us, since all of your answers should be "yes!"

  • Is the HTML generated by all of your PHP files valid according to http://validator.w3.org/?

  • Do your pages detect and handle invalid inputs properly?

  • Are you recording users' histories of transactions properly?

  • Did you choose appropriate data types for your database tables' fields?

  • Are you displaying any dollar amounts to at least two decimal places but no more than four?

  • Are you storing stocks' symbols in your table(s) in uppercase?

Turn It In

  1. When ready to submit, "export" your MySQL database (i.e., save it into a text file) by executing the commands below, where username is your own username, pasting your MySQL password when prompted for a password. (Recall that you can see your MySQL password by clicking the ⓘ icon toward CS50 IDE’s top-right corner.) For security, you won’t see the password as you paste it.

    cd ~/workspace/8-3
    mysqldump -u username -p finance > finance.sql

    If you type ls thereafter, you should see that you have a new file called finance.sql in ~/workspace/finance. (If you realize later that you need to make a change to your database and re-export it, you can delete finance.sql with rm finance.sql, then re-export as before.)

  2. Toward CS50 IDE’s top-left corner, within its "file browser" (not within a terminal window), control-click or right-click your finance folder and then select Download. You should find that your browser has downloaded finance.tar.gz, a "gzipped tarball" that’s similar in spirit to a ZIP file.

  3. Await instructions from your instructor on how to submit the tarball!

This was Problem C$50 Finance (Part 2).