You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -50,11 +50,11 @@ You can preview your work by running `flask run` in the root of your fork and th
50
50
51
51
## 1.1 - Import Flask
52
52
53
-
@pytest.mark.app-import-flask In order to create a flask application Import `Flask` and `render_template` from `flask`.
53
+
@pytest.mark.app-import-flask In order to create a flask application Import `Flask` and `render_template` from `flask` in `jobs/app.py`.
54
54
55
55
## 1.2 - Create a Flask Application
56
56
57
-
@pytest.mark.app-create-flask-app Create an instance of the Flask class called `app`. Pass in the special variable `__name__`.
57
+
@pytest.mark.app-create-flask-app In `app.py` create an instance of the `Flask` class called `app`. Pass in the special variable `__name__` to the `Flask `constructor.
58
58
59
59
## 1.3 - Templates Folder
60
60
@@ -66,15 +66,15 @@ You can preview your work by running `flask run` in the root of your fork and th
66
66
67
67
## 1.5 - Create the Index Route
68
68
69
-
@pytest.mark.app-create-index-route We will display all jobs on the index page. To start we will create a basic route that displays the contents of the index template. Create a function called `jobs` and attach a `route()` decorator with the URL of `/`. Add an additional path of `/jobs`. In the body of the function return a call to the `render_template()` passing the `index.html` template.
69
+
@pytest.mark.app-create-index-route We will display all jobs on the index page. To start in `app.py`we will create a basic route that displays the contents of the index template. Create a function called `jobs` and attach a `route()` decorator with the URL of `/`. Add an additional path of `/jobs`. In the body of the function return a call to the `render_template()` passing in the `index.html` template.
70
70
71
71
## 1.5 - Create Employer and Job Templates
72
72
73
73
@pytest.mark.detail-templates In the root of the `templates` folder, create two files one called `employer.html` and the other called `job.html`.
74
74
75
75
## 1.6 -Create Detail Routes
76
76
77
-
@pytest.mark.app-create-detail-routes We need routes for individual employers and jobs. Create two functions one called `employer` and the other called `job`. Add route decorators to bind these functions with the appropriate URLs:
77
+
@pytest.mark.app-create-detail-routes We need routes for individual employers and jobs. In `app.py` create two functions one called `employer` and the other called `job`. Add route decorators to bind these functions with the appropriate URLs:
78
78
-`/employer` to the `employer` function
79
79
-`/job` to the `job `function.
80
80
@@ -88,7 +88,7 @@ In the body of each function return a call to `render_template()` passing the ap
88
88
89
89
## 2.2 - Add Styles
90
90
91
-
@pytest.mark.add-styles The app will be styled with bulma (bulma.io). Add three link tags to the head of `layout.html`. For the first `href` use mustache syntax`{{}}` and the `url_for()` function to link in the file `css/bulma.css` from the `static` folder. For the second add the file `css/app.css` using the same method. The last link tag should have an `href` value of `https://use.fontawesome.com/releases/v5.2.0/css/all.css`.
91
+
@pytest.mark.add-styles The app will be styled with bulma (bulma.io). Add three link tags to the head of `layout.html`. For the first `href` use the mustache template markup`{{}}` and the `url_for()` function to link in the file `css/bulma.css` from the `static` folder. For the second add the file `css/app.css` using the same method. The last link tag should have an `href` value of `https://use.fontawesome.com/releases/v5.2.0/css/all.css`.
92
92
93
93
## 2.3 - Create Template Files
94
94
@@ -117,148 +117,185 @@ Next create a new folder called `admin` in the `templates` folder, then create t
117
117
118
118
## 2.6 - Navigation
119
119
120
-
@pytest.mark.navigation We want to allow the user to navigate to the admin from the front page. In the `index.html` template file create a link to the main admin page by creating an `<a>` tag nested in the `<div>` with the two classes `columns` and `is-one-fifth`. The `<a>` tag should have an `href` with the value `/admin` and the classes `button`, `is-info`, and `is-pulled-right`. In the `admin/index.html` template file create a link to add a new job by creating an `<a>` tag nested in the `<div>` with the two classes `columns` and `is-one-fifth`. The `<a>` tag should have an `href` with the value `/admin` and the classes `button`, `is-info`, and `is-pulled-right`.
120
+
@pytest.mark.navigation We want to allow the user to navigate to the admin from the front page. In the `index.html` template file create a link to the main admin page by creating an `<a>` tag nested in the `<div>` with the two classes `columns` and `is-one-fifth`. The `<a>` tag should have an `href` with the value `/admin` and the classes `button`, `is-info`, and `is-pulled-right`. In the admin we allow the user to create a new job. In the `admin/index.html` template file create a link to the new job form by creating an `<a>` tag nested in the `<div>` with the two classes `columns` and `is-one-fifth`. The `<a>` tag should have an `href` with the value `/admin/create` and the classes `button`, `is-info`, and `is-pulled-right`.
121
121
122
-
# Module 03 -
122
+
# Module 03 - Database Access
123
123
124
-
## 3.1 -
124
+
## 3.1 - Database Path
125
125
126
-
@pytest.mark.
126
+
@pytest.mark.app-database-path In `app.py` and below the import statements create a constant called `DATABASE` that contains the path to the already created database stored in `db/jobs.sqlite`.
127
127
128
-
## 3.2 -
128
+
## 3.2 - Import Global Namespace
129
129
130
-
@pytest.mark.
130
+
@pytest.mark.app-import-global-namespace To provide access to the database through out the application we are going to create a function that stores a reference to the database connection in the application_context. Before we can do that in the `from flask` statement add `g` to the import.
131
131
132
-
## 3.3 -
132
+
## 3.3 - Global Database Access
133
133
134
-
@pytest.mark.
134
+
@pytest.mark.app-global-database-access At the top of `app.py` create a function called `get_db`. In the body of the function use the `getattr` function to get the `_database` attribute from the `g` object. If `_database` doesn't exist set the default to `None`. Assign the return value of the `getattr` function to`db`. Next test if `db` is `None` if it is set `db` to `g._database` and `sqlite3.connect(DATABASE)`. To make accessing data easier set the row_factory of `db` to sqlite3.row. This will set all rows to named tuples. Return the `db` variable.
135
135
136
-
## 3.4 -
136
+
## 3.4 - Querying the Database
137
137
138
-
@pytest.mark.
138
+
@pytest.mark.app-querying-the-database Lets create a function below the `get_db` function to make it easier to query the database. Call the function `query_db`, have the function accept three parameters: `query`, `args`, and `one`. Set the default of `args` to an empty tuple `()`. Set the default of `one` to `False`. Call the newly created `get_db` function and assign the return value to a `db` variable. Call the `execute` function, passing in the `query` and `args` variables, on the `db` and assign the return value to a variable called `cursor`. `fetchall()` data from the `cursor` and assign it to a variable called `results`. Close the `cursor` with the `close` function. Next test if there are `results` else return `None`. If there are results test if `one` is `True` and return `results[0]` else return `results`.
139
139
140
-
## 3.5 -
140
+
## 3.5 - Close the Connection
141
141
142
-
@pytest.mark.
142
+
@pytest.mark.app-close-the-connection In order to make sure the database connection is closed when the `app_context` is torn down create a function in `app.py` called `close_connection`. Add a parameter called `exception`. In the body of the function use the `getattr` function to get the `_database` attribute from the `g` object. If `_database` doesn't exist set the default to `None`. Assign the return value of the `getattr` function to`db`. If `db` is not `None``close` the `db`. To ensure this function is called when the `app_context` is destroyed use the ``@app.teardown_appcontext` decorator.
143
143
144
-
## 3.6 -
144
+
#Module 04 - Display Jobs and Employers
145
145
146
-
@pytest.mark.
146
+
## 4.1 - All Jobs
147
147
148
-
## 3.7 -
148
+
@pytest.mark.app-jobs In `app.py` locate the `jobs` function. Above the `render_template` function call, call the `query_db`function. Pass in the SQL statement: `'SELECT job.id, job.title, job.description, job.salary, employer.id as employer_id, employer.name as employer_name FROM job JOIN employer ON employer.id = job.employer_id'`. Assign the results of the call to a variable called `jobs`. In the `render_template` function, pass a keyword argument of `jobs=jobs`.
149
149
150
-
@pytest.mark.
150
+
## 4.2 - Individual Job Details
151
151
152
-
## 3.8 -
152
+
@pytest.mark.app-individual-job-details To bring back just one job from the database we are going to use a where clause. In the where clause we will need a `job_id`. We are going to get this from the URL. In `app.py` locate the `job` function. In the route decorator for the function after the url path `/job` add `/<job_id>`. To use this `job_id` we also need to pass it to the job function add `job_id` to the parameter list of the `job` function. Above the `render_template` function, call the `query_db` function and assign the results of the call to a `job` variable. Pass the function three arguments:
153
+
- SQL Query: `'SELECT job.id, job.title, job.description, job.salary, employer.id as employer_id, employer.name as employer_name FROM job JOIN employer ON employer.id = job.employer_id WHERE job.id = ?'`
154
+
- List Literal: [job_id]
155
+
- True: This will bring back only one result.
153
156
154
-
@pytest.mark.
157
+
In the `render_template` function, pass a keyword argument of `job=job`
155
158
156
-
## 3.9 -
159
+
## 4.3 - Individual Employer Details
157
160
158
-
@pytest.mark.
161
+
@pytest.mark.app-individual-employer-details Similar to the `job` function the employer route will only need the details of one employer. Locate the `employer` function in `app.py`. Again we need the unique id of an employer and will receive this from the URL. Add `/<employer_id>` in the route decorator after `/employer`. So that the `employer` function has access to this value add `employer_id`to the parameter list. Make a call to `query_db` and assign tne return value to `employer`. Pass in the arguments:
162
+
- SQL Query: 'SELECT * FROM employer WHERE id=?'
163
+
- List Literal: [employer_id]
164
+
- True: This will bring back only one result.
159
165
160
-
## 3.10 -
166
+
In the `render_template` function, pass a keyword argument of `employer=employer`
161
167
162
-
@pytest.mark.
168
+
## 4.4 - All Employer Jobs
163
169
164
-
# Module 04 -
170
+
@pytest.mark.app-all-employer-jobs On the employer details page we want to display all of the employers jobs. In the `employer` function in `app.py` below the `employer` variable, add a call to the `query_db` function and assign the results to a variable called `jobs`. Pass the function two arguments:
171
+
- SQL Query: `'SELECT job.id, job.title, job.description, job.salary FROM job JOIN employer ON employer.id = job.employer_id WHERE employer.id = ?'`
172
+
- List Literal: [employer_id]
165
173
166
-
## 4.1 -
174
+
In the `render_template` function, add another keyword argument of `jobs=jobs`
167
175
168
-
@pytest.mark.
176
+
## 4.5 - Job Card
169
177
170
-
## 4.2 -
178
+
@pytest.mark.job-card Open the file `templates/_jobs.html` find the `<p>` tag with a class of `card-header-title`. Add an `<a>` tag with an `href` of `{{ url_for('job', job_id=job['id']) }}`. The content should be `{{ job['title'] }}`. Next find the `<div>` with a class of `content`. To this tag add a `<p>` tag and in this tag add the following:
179
+
-`<a>` tag with an `href` of `{{ url_for('employer', employer_id=job['employer_id']) }}`. The content should be `{{ job['employer_name'] }}`. Add line break.
180
+
- ${{ job['salary'] }}. Add line break.
181
+
- {{ job['description'] }}
171
182
172
-
@pytest.mark.
183
+
## 4.6 -Display All Jobs
173
184
174
-
## 4.3 -
185
+
@pytest.mark.display-all-jobs Open the file `templates/index.html` above the `{% endblock %}` add a `<div>` with two classes `columns` and `is-multiline`. In the div add a `for in` loop that loops through all jobs. Use the `{% %}` template syntax, don't forget about ending the `for` loop.. In the `for` loop add a `<div>` with two classes `column` and `is-half`. Too this `<div>` add the following template code:
175
186
176
-
@pytest.mark.
177
-
178
-
## 4.4 -
179
-
180
-
@pytest.mark.
181
-
182
-
## 4.5 -
183
-
184
-
@pytest.mark.
187
+
```
188
+
{% with job=job %}
189
+
{% include "_job.html" %}
190
+
{% endwith %}
191
+
```
185
192
186
-
## 4.6 -
193
+
## 4.7 - Display Individual Job Details
187
194
188
-
@pytest.mark.
195
+
@pytest.mark.display-individual-job-details In `templates/job.html` add a template block called `content` using the `{% %}` template markup. In the template block add the following template code:
189
196
190
-
## 4.7 -
197
+
```
198
+
{% with job=job %}
199
+
{% include "_job.html" %}
200
+
{% endwith %}
201
+
```
191
202
192
-
@pytest.mark.
203
+
## 4.8 - Display Individual Employer Details
193
204
194
-
## 4.8 -
205
+
@pytest.mark.display-individual-employer-details Open `templates/employer.html` as the first thing in the template block add the following HTML:
195
206
196
-
@pytest.mark.
207
+
-`<div>`
208
+
- Nested in the `<div>` add an `<h1>` with the content {{ employer['name'] }}
209
+
- Nested in the `<div>` add a`<div>` with a class of `description`
210
+
- Nested in the description `<div>`add a`<p>` with the content {{ employer['description'] }}
211
+
212
+
## 4.9 -Display All Employer Jobs
197
213
198
-
## 4.9 -
214
+
@pytest.mark.display-all-employer-jobs Open the file `templates/employer.html` below the jobs `<h2>` add a `<div>` with two classes `columns` and `is-multiline`. In the div add a `for in` loop that loops through all jobs. Use the `{% %}` template syntax, don't forget about ending the `for` loop.. In the `for` loop add a `<div>` with two classes `column` and `is-half`. To the `<div>` add the following template code:
199
215
200
-
@pytest.mark.
216
+
```
217
+
{% with job=job %}
218
+
{% include "_job.html" %}
219
+
{% endwith %}
201
220
202
-
## 4.10 -
221
+
# Module 05 - Employer Reviews
203
222
204
-
@pytest.mark.
223
+
## 5.1 - Review Route
205
224
206
-
## 5.1 -
225
+
@pytest.mark.app-review-route In `app.py` below the `employer` function create a new function called review. Add `employer_id` to the parameter list. Add a route decorator will a URL pattern of `/employer/<employer_id>/review` as well as a keyword argument `methods` set to a tuple with two values: 'GET' and 'POST'.
226
+
In the body of the function return the render_template function passing in `review.html` template and a keyword argument of `employer_id=employer_id`.
207
227
208
-
@pytest.mark.
228
+
## 5.2 - Check for POST method
209
229
210
-
## 5.2 -
230
+
@pytest.mark.app-review-check-for-post-method In the body of the `review` above the render_template function call, create an `if` statement that checks if the request method is 'POST'. In the `if` statement create four variables `review`, `rating`, `title`, and `status`. Set them equal to their respective `request.form` values i.e. `request.form['review']`. Create two more variables `error` (set to `None`) and `date` (set to `datetime.datetime.now().strftime("%m/%d/%Y")`) . For `datetime` add an `import datetime` statement to the top of `app.py`.
211
231
212
-
@pytest.mark.
232
+
## 5.3 - Check for Values
213
233
214
-
## 5.3 -
234
+
@pytest.mark.app-review-check-for-values In the body of the post `if` statement in the review function in `app.py`, below the variables check if there is a value for `review`, `rating`, and `title`. If any of these are empty set `error` to an appropriate error message i.e. 'Review field is required.'
215
235
216
-
@pytest.mark.
236
+
## 5.4 - Check for Error
217
237
218
-
## 5.4 -
238
+
@pytest.mark.app-review-check-for-error Still in the review function below the value checks add an `if` statement that checks if `error` is `None`. If there are no errors we are going to add the form values to the database. To do this we need to connect to the database and commit some changes. Follow these steps:
239
+
- Set a `db` variable to a call to `get_db()`
240
+
- `execute` the following SQL statment: `'INSERT INTO review (review, rating, title, date, status, employer_id) VALUES (?, ?, ?, ?, ?, ?)'` passing the values: `(review, rating, title, date, status, employer_id)`
241
+
- `commit` the changes to the database.
242
+
- return a redirect taking the user back to the employer page. **Hint: use `redirect()` and `url_for()` (pass keyword argument of `employer_id=employer_id`) both of which need to be imported from flask.**
219
243
220
-
@pytest.mark.
244
+
If there are errors (`else` statement) `flash` the error. **Hint: `else` statement; use `flash()` (imported from flask)**
221
245
222
-
## 5.5 -
246
+
## 5.5 - Review Form Cancel
223
247
224
-
@pytest.mark.
248
+
@pytest.mark.review-form-cancel Open `templates/review.html` and find the cancel anchor tag. Add an `href` attribute with a value of ``{{ url_for('employer', employer_id=employer_id) }}`.
225
249
226
-
## 5.6 -
250
+
## 5.6 - Individual Employer Reviews
227
251
228
-
@pytest.mark.
252
+
@pytest.mark.app-individual-employer-reviews Now that employer reviews can be created, lets display them on the individual employer pages. Switch back to `app.py` and find the `employer` function below the jobs query add an new query to get all review for the employer. Make a call to `query_db` and assign tne return value to `reviews`. Pass in the arguments:
253
+
- SQL Query: 'SELECT review, rating, title, date, status FROM review JOIN employer ON employer.id = review.employer_id WHERE employer.id = ?'
254
+
- List Literal: [employer_id]
229
255
230
-
## 5.7 -
256
+
In the `render_template` function, add another keyword argument of `reviews=reviews`
231
257
232
-
@pytest.mark.
258
+
## 5.7 - Display Individual Employer Reviews
233
259
234
-
## 5.8 -
260
+
@pytest.mark.display-individual-employer-reviews Open `templates/employer.html` find the review `<h2>` in the empty `{% %}` template tag add a `for in` loop to loop through all `reviews`. Add the `endfor` directive to the second empty `{% %}` template tag. In the `<div>` with a class of `media-left` add this for loop:
261
+
```
262
+
{% for _ in range(1, review['rating']): %}
263
+
<spanclass="fa fa-star checked"></span>
264
+
{% endfor %}
265
+
```
266
+
In the `content <div>` add a paragraph tag. In the paragraph display the details of a review:
267
+
- `title`
268
+
- `status`
269
+
- `date`
270
+
- `review`
235
271
236
-
@pytest.mark.
272
+
For the `Create Review` button to work add an `href` that points to the individual employer page.
237
273
238
-
## 5.9 -
274
+
# Module 06 - Add Jobs
239
275
240
-
@pytest.mark.
276
+
## 6.1 - Admin Route
241
277
242
-
## 5.10 -
278
+
@pytest.mark.app-admin-route We will display all jobs on the admin page. To start, in `app.py` create a basic route that displays the contents of the admin index template. Create a function called `admin` and attach a `route()` decorator with the URL of `/admin`. In the body of the function use the `query_db` function to get all jobs from the database. The SQL can be found in other routes. Next, return a call to the `render_template()` passing in the `admin/index.html` template and the correct keyword arguments.
243
279
244
-
@pytest.mark.
280
+
## 6.2 - Admin Create Job Route
245
281
246
-
## 5.11 -
282
+
@pytest.mark.app-admin-create-job-route In `app.py` create a route at the path `/admin/create` that accepts the methods 'GET' and 'POST'.
247
283
248
-
@pytest.mark.
284
+
## 6.3 - Check for POST method
249
285
250
-
## 5.12 -
286
+
@pytest.mark.app-admin-check-for-post-method In the body of your route check if data has been posted then create four variables `title`, `description`, `salary`, and `employer_id`. Set them equal to their respective `request.form` values. Create an `error` variable set to `None`.
251
287
252
-
@pytest.mark.
288
+
## 6.3 - Check for Values
253
289
254
-
## 5.13 -
290
+
@pytest.mark.app-admin-check-for-values In the body of the post `if` statement in your route function in `app.py`, below the variables, check if there is a value for `title`, and `employer_id`. If either is empty set `error` to an appropriate error message i.e. 'Title field is required.'
255
291
256
-
@pytest.mark.
292
+
## 6.4 - Check for Error
257
293
258
-
## 5.14 -
294
+
@pytest.mark.app-admin-check-for-error Still in your route function below the value checks add an `if` statement that checks if `error` is `None`. If there are no errors we are going to add the form values to the database. Connect to the database and commit the changes.
295
+
**Hint: The SQL is `'INSERT INTO job (title, description, salary, employer_id) VALUES (?, ?, ?, ?)'`**
259
296
260
-
@pytest.mark.
297
+
If there are errors `flash` the error. **Hint: `else` statement**
261
298
262
-
## 5.15 -
299
+
## 6.5 - Admin Navigation
263
300
264
-
@pytest.mark.
301
+
@pytest.mark.admin-navigation Find the admin index template and add an href attribute to the `New Job` link. Send the user to the URL ``/admin/create`. Open create job template and find the cancel anchor tag. Point the link back to the admin page using `url_for()`.
0 commit comments