T-Th 9:05 |
CS 1110: Introduction to Computing Using Python Spring 2014 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Main
About: Announcements Staff Times & Places Syllabus Materials: Texts/iClickers Python Command Shell Terminology VideoNote Handouts: Lectures Assignments Labs Assessment: Grading Exams Resources: CMS Piazza AEWs FAQ Python Tutor Python Library Style Guide Academic Integrity |
Assignment 1:
|
Code | Name | 1 USD = | Code | Name | 1 USD = | |
---|---|---|---|---|---|---|
AED | United Arab Emirates dirham | 3.67300014 | MAD | Moroccan dirham | 8.42296775 | |
ANG | Netherlands Antilles guilder | 1.73999892 | MDL | Moldovan leu | 12.7995085 | |
ARS | Argentine peso | 5.7032052 | MKD | Macedonian denar | 46.8999156 | |
AUD | Australian dollar | 1.08131488 | MUR | Mauritian rupee | 31.000062 | |
BGN | Bulgarian lev | 1.47550078 | MXN | Mexican peso | 13.1008371 | |
BHD | Bahrain dinar | 0.377020026 | MYR | Malaysian ringgit | 3.2790003 | |
BND | Brunei dollar | 1.27009932 | NAD | Namibian dollar | 9.98801438 | |
BOB | Bolivian boliviano | 6.90999046 | NGN | Nigerian naira | 162.601626 | |
BRL | Brazil real | 2.27679964 | NIO | Nicaraguan cordoba | 24.500196 | |
BWP | Botswana pula | 8.58369099 | NOK | Norwegian krone | 6.02188352 | |
CAD | Canadian dollar | 1.03769963 | NPR | Nepalese rupee | 102.658865 | |
CHF | Swiss franc | 0.93340004 | NZD | New Zealand dollar | 1.24672734 | |
CLP | Chilean peso | 507.099391 | OMR | Omani rial | 0.385019952 | |
CNY | Chinese yuan | 6.11931439 | PEN | Peruvian nuevo sol | 2.79949721 | |
COP | Colombian peso | 1945.52529 | PGK | Papua New Guinean kina | 2.58397933 | |
CRC | Costa Rican colon | 512.820513 | PHP | Philippine peso | 43.9000834 | |
CZK | Czech koruna | 19.4791281 | PKR | Pakistan rupee | 104.810816 | |
DKK | Danish krone | 5.62720868 | PLN | Polish zloty | 3.21870454 | |
DOP | Dominican peso | 42.6493795 | PYG | Paraguayan guarani | 4464.28571 | |
DZD | Algerian dinar | 82.0411847 | QAR | Qatar riyal | 3.63939426 | |
EEK | Estonian kroon | 11.841186 | RON | Romanian lei | 3.37350048 | |
EGP | Egyptian pound | 6.89541041 | RSD | Serbian dinar | 86.8734254 | |
EUR | Euro | 0.75443229 | RUB | Russian ruble | 33.0753456 | |
FJD | Fiji dollar | 1.86671645 | SAR | Saudi riyal | 3.7506001 | |
GBP | British pound | 0.637389254 | SCR | Seychelles rupee | 12.0209646 | |
HKD | Hong Kong dollar | 7.75422217 | SEK | Swedish krona | 6.55011823 | |
HNL | Honduran lempir | 20.389854 | SGD | Singapore dollar | 1.27009932 | |
HRK | Croatian kuna | 5.72780333 | SKK | Slovak koruna | 22.7277893 | |
HUF | Hungarian forint | 226.75737 | SLL | Sierra Leonean leone | 4310.34483 | |
IDR | Indonesian rupiah | 11494.2529 | SVC | Salvadoran colon | 8.74997813 | |
ILS | Israeli shekel | 3.60829764 | THB | Thai baht | 32.1502058 | |
INR | Indian rupee | 64.3293664 | TND | Tunisian dinar | 1.654799 | |
JMD | Jamaican dollar | 101.677682 | TRY | Turkish lira | 2.02820009 | |
JOD | Jordanian dinar | 0.707200075 | TTD | Trinidad dollar | 6.41000987 | |
JPY | Japanese yen | 99.9100809 | TWD | Taiwan dollar | 29.5963064 | |
KES | Kenyan shilling | 87.5503414 | TZS | Tanzanian shilling | 1618.12298 | |
KRW | South Korean won | 1084.5987 | UAH | Ukrainian grivna | 8.16499829 | |
KWD | Kuwaiti dinar | 0.284910025 | UGX | Ugandan shilling | 2570.69409 | |
KYD | Cayman Islands dollar | 0.820000131 | USD | U.S. dollar | 1 | |
KZT | Kazakh tenge | 153.515505 | UYU | Uruguayan peso | 22.4351065 | |
LBP | Lebanese pound | 1510.57402 | UZS | Uzbekistani sum | 2123.14225 | |
LKR | Sri Lankan rupee | 132.908028 | VND | Vietnamese dong | 21276.5957 | |
LTL | Lithuanian litas | 2.60369881 | YER | Yemeni rial | 214.915109 | |
LVL | Latvian lats | 0.529799921 | ZAR | South African rand | 9.98801438 | |
MAD | Moroccan dirham | 8.42296775 | ZMK | Zambia kwacha | 5208.33333 |
Note however, that you should not use this table in any of the functions
that you write in a1.py
. The table above is for testing your
functions; not for writing them. There is no reason for you to waste your
time hard-coding in all of the currencies listed in this table into your
program, since the web service you will contact already knows them all anyway.
One of the most important outcomes of this assignment is that you understand the importance of testing. This assignment will follow an iterative development cycle. That means you will write a few functions, then fully test them before you write any more. This process makes it easier to find bugs; you know that any bugs must have been part of the work you did since the last test.
In this section we help you get started with this process. We also provide an overview of the rest of the assignment.
To do this assignment, Python must be set up properly. If you have not already done this, follow the installation instructions to set it up on your computer. Alternatively, you can just work in the ACCEL lab.
You should also create a folder on your hard drive that is dedicated to this assignment and this assignment only. Every time that you work on a new assignment, we want you to make a new folder, to keep things organized and avoid problems with naming collisions. Make sure that the command shell and Komodo Edit are both open in the current folder before you start.
a1
In your newly created directory, you should create the module a1
(with file name a1.py
). This will be the main module for this assignment.
Following the
style guidelines, the first
three lines of this file should be single-line comments with (1) the module name, (2) the
name and netid of the authors, and (3) the date the file was last editted. Immediately
after this, add the following docstring:
"""Module for currency exchange
This module provides several string parsing functions to
implement a simple currency exchange routine using an
online currency service. The primary function in this
module is exchange()."""
This docstring is the module specification. We recommend that you cut-and-paste the docstring into a1. For now, we want to expose you to specifications, not have you write them on your own.
a1test
Iterative development hinges on proper unit testing, which was covered in
lecture and
lab. In the same folder
as a1.py
, create the module a1test
(with file name a1test.py
). This will be the unit test for the
a1
module.
As with a1.py
, the first three lines of this file should be single-line
comments with (1) the module name, (2) your name and netid, and (3) the date the file
was last editted. Immediately after this, add the following Python code:
"""Unit test for module a1 When run as a script, this module invokes several procedures that test the various functions in the module a1.""" import cornelltestfrom a1 import *import a1
If you don't have the cornelltest module, you can get it
here.
As in lab 3
, the module
cornelltest
provides access to the functions assert_equals
and assert_true
. The second import statement allows the unit test to
access all of the functions in [As discussed in lecture, since it is so
important to understand where everything is, it is best at this
point to explicitly include the module name in calls to things in the module.]
a1
, and do it without putting the name
in front of each function.
Add four procedure stubs to this assignment: testA
, testB
,
testC
, testD
. Remember that a procedure stub should have the
keyword pass
(indented) after the header, but nothing else. We will add
our test cases to these procedures later.
Finally at the end of a1test
, add the following script code:
See the lecture slides on how application code works.if __name__ == '__main__': testA() testB() testC() testD() print "Module a1 passed all tests"
The script code will call your four test procedures, which are (currently) empty. If everything is working, then the module print out the message
"Module a1 passed all tests"
Try this out.
The rest of the assignment is broken into four parts (listed as Parts A, B, C, and D). In each part, do the following:
We will give you the header to write. We will also give you a detailed docstring specification for the function. You should copy-and-paste the specification into the function body, indented.
Yes, this means you are writing tests before writing the function bodies. We talked about this in lecture.
Unless otherwise instructed, each test case should be a call to an assert function in cornelltest. Furthermore, your tests should be representative. Refer back to the instructions for lab 3 if you do not understand what we mean by this.
Make sure that the function satisify the specifications exactly. If the specification says to return something, you need a return statement. Make sure that the value returned is of the correct type.
If errors are found, fix them and re-test. Keep doing this until no more errors are found.
The descriptions that we provide in each part below represent the level of completeness and precision we are looking for in your docstring comments. In fact, it is best to copy-and-paste these descriptions to create the first draft of your docstring comments. If you do not cut and paste, please adhere to the conventions we use, such as using a single line, followed by a blank line and a more descriptive paragraph, or by using "Returns: ..." for fruitful-functions. Using a consistent set of good conventions in this class will help us all.
If you want to see if your specifications are written correctly, start an interactive Python shell and type
This should list all the functions with their specifications.>>> import a1 >>> help(a1)
A large part of this assignment is breaking up a JSON string-value pair. Conceptually, you want to separate the string "v" and the numeric currency amount from the pair. For example, if we are given the string
'"v": 1.88608072500000'
Then we want to break it up into '"v"' and '1.88608072500000'.
This is the motivation for the after_space function below.
Returns: Substring of s after the first space
Precondition: s has at least one space in it
Implement this function according to the specification, as described in the Instructions for the Remainder of the Assignment. In other words,
To test the functions, you should make use of assert_equals in the
module cornelltest to compare the result of each function with the
string that you expect to get back. Our unit test has four test cases
for each of the two functions the function above. When you think about what test cases
you want to include, consider the following:
Do not forget to add a specification to testA(). Just because it is used for testing does not mean that it should not be properly specified.
The implementation of after_space() should be relatively simple. We were able to implement it in one or two lines.
All of the valid responses to a currency query contain the keyword string '"v"'. Our goal here is to extract the string-value pair containing these keywords from the response string. For example, from a given response JSON string
'{"to": "EUR", "rate": 0.75443, "from": "USD", "v": 1.88608072500000}'
we want to extract the string-value pair containing "v"
"v": 1.88608072500000
Note that the string-value pair is the substring starting from a double quote '"' of the keyword string, and before the terminating right brace '}'.
While working on each of the functions below, remember to write the test cases in
a1test.py
before implementing the body. All test cases in this section
go in the procedure testB(), which you should remember to specify. You
should thoroughly test each function before implementing the next one.
Returns: The index of the keyword string '"v"' including the two (double) quote characters
A quote character is one that is inside a string, not one that delimits it. We typically use single quotes (') to delimit a string if want to use a double quote character (") inside of it.
Example: If s is 'A "v" B'
, this function returns
2
Example: If s is 'A v B "v" C'
, this function returns
6
because it only picks the keyword v
with the quotes around it.
Precondition: s is a string with the keyword string '"v"' inside.
Once you have this function completed, you should move on to the following functions.
Returns: The boolean value that tells whether a currency query is invalid.
Given a JSON response to a currency query, this returns the boolean value that tells whether a currency query is invalid. If the response is a string of the form
'{"err": "<error-description>"}'then this function returns boolean
True
.
For example, if you pass a query with invalid currency code, you will get this:
'{"err": "failed to parse response from xe.com."}'But, note that the sub-string <error-description> is subject to the type of error, and not restricted to the example above.
Precondition: query is the response to a currency query, contains 'err' string-value pair if and only if query is invalid.
Returns: The float-type v value of keyword "v" in the response to a currency query.
Given a JSON response to a currency query, this returns the numeric value following the keyword v. For example, if the JSON is
'{"to": "EUR", "rate": 0.75443, "from": "USD", "v": 1.88608072500000}'
then this function returns the floating type numeric
1.88608072500000
(not a string).
Precondition: query is the response to a valid currency query. 'v' string-value pair always comes last
As always, write your unit tests before implementing the two functions. Look carefully at the specifications. You only need to test valid JSON queries. To get some JSON responses for testing, enter a query URL into the web service and copy the result into a test case.
You should not need a conditional statement to implement these functions; simply find
the position of the appropriate keyword and extract the value
in quotes immediately after it. Your implementation must make use of the
find() or index()
string methods, plus the helper function after_space()
.
Now it is time to interact with the web service. In this part, you will implement a single function. The test cases for this function should go in procedure testC() in a1test.py. Do not forget to specify testC() properly.
Returns: A JSON string that is a response to a currency query.
A currency query converts amount_from money in currency currency_from to the currency currency_to. A valid response should be a string of the form
'{"to": "<original-code>", "rate": <conversion-rate>, "from": "<target-code>", "v": <new-amount>}'where the code values contain strings of currency code for the original and new currencies, and the others for the rates and the new amount of conversion. If the query is invalid, a response should be a string of the form
'{"err": "<error-description>"}'
Precondition: amount_from is of type float, while currency_from and currency_to are of type string
While this function sounds complicated, it is actually the simplest function so far and
can be implemented in two lines. You need to use the
urlopen
function from the module urllib2
. This function takes
a string that represents a URL and returns an instance of the class addinfourl that represents
the web page for that url. This object has the following methods:
Method | Specification |
---|---|
geturl() | Returns: The URL address of this web page as a string. |
read() | Returns: The contents of this web page as a string. |
Using one or both of these methods (you might not need them both) is enough to implement the function above.
You need to ensure that the function returns exactly the right JSON
string for the value given. The best way to test this is to use a web
browser to manually get the right JSON answer. such as
For example, one test case can
be constructed by seeing the result of going to the URL
http://cs1110.cs.cornell.edu/2014sp/a1/calculator.php?from=USD&to=EUR&q=2.5
Copy the value from this web page into a test case in testC()
.
Then check that the function returns the same JSON string. Remember
to be thorough with your choice of test cases; one is not enough.
Important: Fetching a web page takes time, especially if too many people are trying to do so at the simultaneously. You should give each call to this function at least 5-10 seconds to complete before restarting any tests.
We are now ready for the final part of the assignment. Implement the following specifications, again using our test-case-before-function-body approach. The test cases should go in procedure testD() in a1test, which you should properly specify. You may wish to use assert_true() instead of assert_equals() in some of your test cases. As with lab 3, there is also a case in which you will want to use assert_floats_equal().
Returns: True
if
currency is a valid (3 letter code for a) currency. It returns
False
otherwise.
Precondition: currency is a string.
In implementing iscurrency()
, you should not
use the table of currencies. That would make a
very large function with a lot of if-statements. You are not allowed if-statements
in this lab. Instead, you
must use the functions currency_response
and get_error()
as helper methods
.
Returns: amount of currency received in the given exchange.
In this exchange, the user is changing amount_from money in currency currency_from to the currency currency_to. The value returned represents the amount in currency currency_to.
The value returned has type float.
Precondition: amount_from is a float. Both currency_from and currency_to are strings with valid three-letter currency codes.
In the case of iscurrency()
, you will find the
exchange table useful in determining correct
answers for your test cases. While it is not okay to use the table in the
body of iscurrency()
itself, it is okay to use the table to
to decide on some test cases.
You may also use the table to craft some test cases for the function
exchange
. However, you might find it easier to use a currency
query URL to look up the correct answer, and then paste the answer into your
test case.
A bigger issue with testing exchange
is that problem that we
saw in class: real numbers cannot be represented exactly. This
creates problems when you try to test equality between floats.
To solve this problem, cornelltest provides a function called
assert_floats_equal(), which you encountered in
lab.
You should use this function to test exchange() instead of
assert_equals().
Finally, bear in mind that, like currency_response
, these functions
connect to the web service, and so are not instantaneous. In our solution,
with complete test procedures for everything, it can take up to 5 seconds to
run the unit test on campus. This will be a bit slower if you are working
closer to the deadline.
Once you have everything working you should go back and make sure that your program meets the class coding conventions, including the following:
In the case of long lines, note that Python allows you to "hit Return" within any expression inside of parentheses. So if you have a long string expression of the form
string1 + string2 + ... + stringkyou can break it up over several lines, using parentheses, as follows:
(string1 + string2 + ... + stringi+ stringj + ... + stringk)You might find this useful if your lines are over the 80 character limit.
Upload the files a1.py
and a1test.py
to CMS by
the due date: Tuesday, February 25th at 11:59 pm.
Do not submit any files with the extension/suffix .pyc
. It will
help to set the preferences in your operating system so that extensions always appear.
Check the CMS daily until you get feedback from a grader. Make sure your CMS notifications for CS 1110 are set so that you are sent an email when one of your grades is changed. To find the feedback, click on the Assignment 1 link in CMS. On the page you are brought to, click on the red word "show" in the line "Grading Comments & Requests (show)." You can contact your grader if you have questions about their feedback; you can see their netid in the place you can see their feedback.
Within 24 hours, do RRRRR: Read the feedback, Revise your program accordingly, Resubmit, and Request a Regrade using the CMS. If you do not request a regrade, we have no simple way of knowing that you have resubmitted.
This whole process, starting from first submission on Tuesday, February 25th, continues until you submit a solution that demonstrates complete mastery; in some cases this may require multiple additional resubmits by you. You need to complete this process within one week. You need to have submitted a final, correct version by Tuesday, March 4th, which means you will probably want to have re-submitted at least once before then.
In addition to turning in the assignment, we ask that you complete the new survey posted in CMS. These assignments are still rather new(ish), and we would like some understanding of how long you spent on the assignment, your impression of the difficulty, and what could be done to improve it.
Please try to complete the survey within a day of turning in this assignment. Remember that participation in surveys compromise 1% of your final grade. We also ask that you be honest in your answers.
This section is not part of the assignment. It is optional. Furthermore, do not make the changes in this section to the file that you submit for grading. It will be sent back to you to fix.
So far you have worked with a simulated currency exchange service. But with a few changes you can use the real thing. In the web service instructions we told you to use the URL prefix
http://cs1110.cs.cornell.edu/2014sp/a1/calculator.php?If you change that prefix to
http://rate-exchange.appspot.com/currency?you will use Rate-Exchange currency calculator instead. Try that out on converting dollars to Euros (pick small values for now).
Run the exchange function four or five times. See the value being different from our currency table? That is one of the reasons we did not use the service; it is too hard to test against. In fact, even professional software engineers would do what we did: write a program against an unchanging exchange service before deploying it against the real thing.