clone down this repo.
Note, you must work LOCALLY for this assignment, as you wonât be able
to see the images when youâre on the Pi (or at least, I donât think you can as
easily.)
Next, slowly and carefully read through the whole set of
directions here
Then, in the project you cloned down, read through filter.c for a few
minutes, try to understand as much as possible of what theyâre doing in that
file. If you donât understand it all, thatâs fine, but try to figure out as
much as possible, at least at a high level. Remember you can man c
functions, so if you see a function, you can get documentation on it by typing
man <functionname>, like man fread
Look at lines 90-115 of filter.c â this is where they basically switch on
the command line flag that selects which type of filter (blur, grayscale,
reflect, etcâŠ) and then call one of the functions in helpers.c. The
functions in helpers.c (as described in the cs50 website) are the only
things you need to change.
In each filter function, you get handed a width, height, and a
multidimensional array of pixels. Each pixel is represented by a RGBTRIPLE
struct, containing a color, which is a mixture of red, green, and blue, each
with a value of 0-255 (or 0x00 - 0xff in hex)
Because arrays decay into pointers when passed to functions, you can think
of it like the array of pixels youâre getting is passed by reference,
which means that if you alter the pixel color data, the data will be altered
for the calling function (which is main). So basically, you get a chance to
twiddle the RBG values of each pixel, and then the main function will write
out the altered bitmap to disk for you.
You can test your work like so: letâs say you start with the grayscale
filter. First, make the code by typing make. Then run
./filter -g images/yard.bmp out.bmp. Then, open a finder window and check
the out.bmp file. Because your filter is empty right now, it should just be
an exact copy of the images/yard.bmp file. But, as you start doing actual
pixel manipulation in your functions, youâll see the output file begin to
change.
Complete the grayscale, sepia, reflect, and blur filters. (in that
order!)
Hint 1: You may find it very helpful to make one or more helper functions for
some of the harder filters.
Hint 2: For the blur and edges filter, you canât just overwrite pixels
while processing each pixel, becuase the next pixel needs to know the original
values of the surrounding pixels to calculate itâs value. This means
youâre going to need to loop through every pixel twice. The first time
calculating the pixels that you would like to overwrite but canât (instead
storing them in some temporary variable), and the second time taking the
calculated pixel values out of your temporary storage and overwriting the
original pixel data.
Extra Credit: âš Complete the edges filter.
Commit your work, push up a MR, and your MR, upload screenshots of each
filter type you completed, review your diffs and cleanup! and Slack me
the MR URL.
Monkey #27 - Cleaning Rampage Edition đ đ§Œ
Address all feedback from prior MRâs, and merge.
Create a new branch.
Double-check that you did the first two steps.
For this one, just watch my video and do the same basic steps in your code.
This is not stuff he shows in the book, itâs just that I decided it was time
to clean up and quasi ânamespaceâ all our imports, as youâll see.
Submit a MR, Review your diffs and fixup!!, then slack the MR url
Monkey #28 đ
Address all feedback from prior MRâs, and merge.
Create a new branch.
Double-check that you did the first two steps.
Start where we left of last time, after the cleaning rampage edition, which is
roughly at the middle of page 143 (online version), or page 181 (print
edition), where it ways Easy right? The test passes.
work through the rest of 3.10 Functions and Function Calls
Commit your work.
As always, make sure to try to do as much as you can without the videos, but
always also watch the videos and update your code to (mostly) match.
Submit a MR, Review your diffs and fixup!!, then slack the MR url
Flashcards Webapp
Do this homework BEFORE the API side.
Make sure youâve addressed all feedback from last weekâs MRs (both API and
Webapp), and have merged.
Open up your Webapp repo, and make a new branch.
Read all of the instructions below before starting step by step.
This weekâs feature is to require login/authentication, and have it work with
the API. Hereâs some bullet points of what I mean by that:
Add a new piece of top level state to your app, with the key of token
which has the type of string | null
We want this piece of state available immediately, so find the place where
you create your âinitial stateâ, set token to the result of calling the
getUserToken function you made in last weeks assignment.
At or near the top of your component heirarchy, we need to check if there
is a token present (by looking into the redux state). If the token is not
there, then you should redirect to the /login router. You might need to
research on the React Router docs website to figure out how to properly
redirect a request. (Hint: I think the easiest way to do this is actually
render a component that redirects or navigates you to another route. Check
this page out).
Youâll need to wire up your <Login /> component so that it actually does
stuff now. For instance, youâll need to track (in state) the email address
thatâs being entered, and the password. I will let you decide if you want to
keep the email address and password in the redux state, or in local state to
this component. I think either is acceptable. If you use local state, that
means using useState() from react, instead of dispatching actions and
changing your main State type to accomodate the fields. But putting them
inside redux is great too, your choice.
When the submit button is clicked, you should disable the submit button
while the request to the API is in flight.
When the âsubmitâ button is clicked, youâll need to dispatch an action which
is a thunk. So that you can do async fetch things. In that thunk, youâll
be sending the login request to the API and waiting to see if you get a token
back.
If the thunk stuff sounds complicated, remember that you already have one
thunk action in your system, the one that loads the cards. Model this thunk
after that one, if it helps.
Its been a while since youâve POST-ed a json-body with fetch, if we ever
even did it. Hereâs a bit of example code of how to send JSON and headers with
fetch â this isnât meant to be copy-pastable by you, but it should show you
all the moving parts so you can adapt it to work for your app:
const response =awaitfetch(`https://api.cats.com/cats`,{method:`POST`,// <-- here's where you set it to be POSTheaders:{// vvv-- this header is required when sending JSON body"Content-Type":`application/json`,Beep:`boop`,// <-- other headers go here},// the `body` prop is a STRING created by calling// JSON.stringify on some data you want to send as JSON:body:JSON.stringify({beep:`boop`,herp:true}),});
If the login request comes back successfully, youâll want to dispatch an
action that you âreceived a tokenâ. The reducer should take the token recieved
and set it in state.
Also, after you receive a token and dispatch an action that you got it, you
should redirect to/, so the cards will be visible.
hereâs some psuedo-code to help you with the login thunk. Everywhere you see a
??? would be places you need to fill in logic and code.
exportfunctionlogin(): Thunk {returnasync(dispatch, getState)=>{// 1) get `email` and `password` from `getState()`// unless you used local state, in which case they// should be passed as ARGUMENTS to `login()`let state =getState();let email =`???`;let password =`???`;// 2) make the login request to the APItry{// for hints on the innerds of `fetch`// see previous code snippetlet response =awaitfetch(`???`);let json =await response.json();// 3) get the token from `json`let token =`???`;// 4) store the user token for next time`???`;// 5) dispatch that you got a token// you'll need to MAKE the `receivedToken`// action creator, and handle it as welldispatch(receivedToken(token));// 6) redirect to `/`// ???}catch(error:unknown){// 7) handle a failed login with an alert
window.alert(`???`);}};}
when you have a token, youâll need to send it to the API so that it can
send back the right cards. To do that, weâre going to use an authentication
pattern called a bearer token. With bearer tokens, you send a header
called Authorization with a value of Bearer <some-token-value>. When using
window.fetch that means passing a header object like this:
The API will use that header to determine if youâre allowed to retrieve cards,
and which cards to pull from the db, in the next assignment.
Also, make it so that if the API errors, or the login info is bad, that some
message is presented to the user (see the window.alert) section in the thunk
skeleton above. But more than just showing an alert, you need to make sure
that the âsubmitâ button un-disables and the user stays on the login
screen, so they can try again.
I might have missed something here youâll need to do to make this work, Iâm
not trying to be exhaustive or even chronological in my steps, just dumping
out things I can think of, and stuff that will be helpful. Youâll need to
think through the whole feature and fill in the gaps.
When youâre done, commit your work, push a MR.
Carefully review your diffs, and clean up anything sloppy or wrong, or
debugging code (as always!).
Push up a draft Netlify deployment
Slack your MR url and your Netlify draft URL.
Flashcards API Assignment
Make sure youâve done the WebApp assignment first.
Make sure youâve addressed and resolved any feedback from previous weekâs API
homework.
Make a new branch.
The high-level description of what weâre doing for this chunk, is that weâre
going to now be requring that we get a Bearer authorization token when someone
tries to hit the /cards route. Weâll use that token to decide if theyâre
allowed to query cards, and which cards to get.
You already have a âroute responderâ called getCards(). Currently it takes
only the db interface, but weâre going to change that. It should now also
take an argument called authorizationHeader which has the type of
string | undefined. Add that argument, and supply a default value for the
argument of undefined.
You should already have at least 2 tests for this route responder, although
they will have typescript errors as soon as you change the signature of the
function. Comment out the existing tests temporarily, and then Add TWO
tests, and make them pass:
if authorizationHeader is undefined, return a 403 status with some
sort of âunauthorizedâ json message
if authorizationHeader is a string, but does not match the format
Bearer <TOKEN>, return a 403 status with some sort of âunauthorizedâ
json message
NOTE: in both cases covered by these two new tests, the database should
never be queried because if we donât have a good token, thereâs no reason
to query. Update your tests to PROVE that the database is never queried.
Now, the next thing we need to do is extract the UUID from the bearer token,
and pass it to our database abstraction. Itâs going to use that token to
authenticate the user and get the right cards, all in one fell swoop.
Change the databaseâs getAllCards() method to become
getUserCards(token: string). Youâll need to update the typescript
interface for the db abstraction, and your Mock database.
Change the getCards() route responder so that it calls this modified
db.getUserCards(token: string) method, passing the token you extracted from
the authorization header.
Next, weâre going to modify the SQL in this getUserCards(token: string)
method. What we want to do is only return cards for the user whose owns the
token passed in. And, we can do this in ONE query.
Pull up Sequel Ace/Pro, and try to work out a single query that pulls just the
users cards, based on the user_id column of the row matching the passed in
token. This is hard to do, donât be discouraged, stroke your beard a bit and
think, and experiment to see if you can figure it out.
If youâre totally stuck after giving it some real effort, refer to
hint #1 and then go back to
Sequel Ace and spend some more time trying to figure it out.
If youâre still stuck, refer to
hint #2 for help.
Once youâve got the query working add a request to your api.http file to
test the request.
Now, uncomment the tests you commented out in step 6, and fix them. They
should be covering these two cases (you can pass dummy auth headers in for
these tests, as long as theyâre in the right format):
the db errors for any reason, which should return a 500 status and some
error json
the happy path, you get some cards back
Finally, in your src/index.ts fix the call site to your route responder by
passing req.headers.authorization to the route responderâs getCards
function. req.headers is an object containg all of the request headers, so
req.headers.authorization is the authorization header (if present). It has
the exact type we want: string | undefined. Fancy that!
Fire up your web app and make sure it can talk to your API and that your
web-app still works.
commit your work, submit a MR, carefully review your own code and clean up/fix
anything you notice, then slack me the MR url.