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
Copy file name to clipboardExpand all lines: cron_jobs_in_flask.md
+16-16
Original file line number
Diff line number
Diff line change
@@ -42,7 +42,7 @@ class Config(object):
42
42
43
43
My configurations above utilize Twilio's SendGrid API to work with emails, especially on a production server. It still works locally though. If you are not familiar with Twilio SendGrid, check out [this tutorial](/twilio_sendgrid/00_overview.md) to learn more. Alternatively, you can use Google's `smtp` for this. Learn how to work with Google's `smtp`[here](email_support_in_flask.md).
44
44
45
-
Since these variables are sourced from the environemnt, it is best practice to add them in a secret `.env` file which should never be committed to version control.
45
+
Since these variables are sourced from the environment, it is best practice to add them in a secret `.env` file which should never be committed to version control.
46
46
47
47
```python
48
48
# .env
@@ -111,9 +111,9 @@ class Client(db.Model):
111
111
112
112
The model has an additional column called `num_newsletter` whose integer values will be used to determine if a user has received an email newsletter or not. Additionally, this column will be used to determine what email newsletter needs to be sent out.
113
113
114
-
It is good practice to respect user's contact information. The fact that a user has shared an email address with us does not give the opportunity to spam them. Hence, if a user wishes to optout of the subscription, they can do so. The application will remember their subscription state using the value in `active` column.
114
+
It is good practice to respect users' contact information. The fact that a user has shared an email address with us does not give us the opportunity to spam them. Hence, if a user wishes to opt-out of the subscription, they can do so. The application will remember their subscription state using the value in `active` column.
115
115
116
-
### Email template
116
+
### Email Template
117
117
118
118
Here is a sample method used to send out an email.
The `send_week1_newsletter()` function uses both text and HTML content to send out the email. This is similar to the `render_template()` function from flask. It is always advisable to have the text template in the event the email server does not entirely parse the HTML content or there is an unexpected issue.
147
+
The `send_week1_newsletter()` function uses both text and HTML content to send out the email. This is similar to the `render_template()` function from the flask. It is always advisable to have the text template in the event the email server does not entirely parse the HTML content or there is an unexpected issue.
148
148
149
149
150
150
### Sending An Email
@@ -193,7 +193,7 @@ def week2_newsletter():
193
193
db.session.commit()
194
194
```
195
195
196
-
Notice that the two functions begin by checking of a user is still subscribed to receive emails. The starting value to know what email needs to be sent out is 0. This value is added to the database when the user is being registered.
196
+
Notice that the two functions begin by checking of a user is still subscribed to receive emails. The starting value to know what email needs to be sent out is 0. This value is added to the database when the user is registered.
197
197
198
198
```python
199
199
if form.validate_on_submit():
@@ -202,19 +202,19 @@ if form.validate_on_submit():
202
202
db.session.commit()
203
203
```
204
204
205
-
0 means that this user is newly registered. If so, then week 1 email newsletter will be sent out to them. As soon as this email is sent out, their status changes to 1, meaning, they should not receive any week 1 newsletter. They are now qualified to receive week 2 newsletter.
205
+
0 means that this user is newly registered. If so, then the week 1 email newsletter will be sent out to them. As soon as this email is sent out, their status changes to 1, meaning, they should not receive any week 1 newsletter. They are now qualified to receive the week 2 newsletter.
206
206
207
207
208
208
## Application Context
209
209
210
-
You have probably come across the command `flask shell`. "Shell" is a command from flask. There is also `flask run` which is used to start the flask server. And many more. It is also possible to create custom commandline operations that suite our needs.
210
+
You have probably come across the command `flask shell`. "Shell" is a command from flask. There is also `flask run` which is used to start the flask server. And many more. It is also possible to create custom command-line operations that suit our needs.
211
211
212
-
In the context of sending periodic email newsletters, we are going to create a command which will run in the application's context such that every time it is invoked, not only will it send out an email, but it will also allow for access to the application's resources such as the database, which me mostly need here.
212
+
In the context of sending periodic email newsletters, we are going to create a command which will run in the application's context such that every time it is invoked, not only will it send out an email, but it will also allow for access to the application's resources such as the database, which we most need here.
213
213
214
214
215
215
### Create a Custom Command
216
216
217
-
Following the principle of separation of concerns, a new module called `cli` will define all that we need for our commandline operations.
217
+
Following the principle of separation of concerns, a new module called `cli` will define all that we need for our command-line operations.
218
218
219
219
```python
220
220
# app/cli.py
@@ -244,7 +244,7 @@ def register(app):
244
244
print(str(datetime.utcnow()), 'Week 2 emails sent to all subscribed clients\n\n')
245
245
```
246
246
247
-
Flask uses [Click](https://click.palletsprojects.com/) for all commandline operations. In the example above, I have created a root command called `send-newsletter-email` from whom subcommands are derived. These sub-commands are created via `app.cli.group` decorator. All that the subcommands do is to invoke the necessary functions used to send out an email. To ensure that I know when the emails were sent out, I have added a `print()` statement.
247
+
Flask uses [Click](https://click.palletsprojects.com/) for all command-line operations. In the example above, I have created a root command called `send-newsletter-email` from whom subcommands are derived. These sub-commands are created via the `app.cli.group` decorator. All that the subcommands do is invoke the necessary functions used to send out an email. To ensure that I know when the emails were sent out, I have added a `print()` statement.
248
248
249
249
250
250
### Test Access to Custom Commands
@@ -310,7 +310,7 @@ Once the job is written and tested, we can now implement the scheduling part. He
310
310
311
311
Here, I am running the `crontab` command under my computer's user, who is typically the same user that runs the flask application. This ensures that the task runs with the correct permissions. It is advisable to not run this command as a root user.
312
312
313
-
Once the file is opened, you will notice that it comes with commentedout instructions. These instructions offer guidance on how to go about scheduling a task.
313
+
Once the file is opened, you will notice that it comes with commented-out instructions. These instructions offer guidance on how to go about scheduling a task.
314
314
315
315
```python
316
316
# ┌───────────── minute (0 - 59)
@@ -324,7 +324,7 @@ Once the file is opened, you will notice that it comes with commented out instru
324
324
# * * * * * <command to execute>
325
325
```
326
326
327
-
Each line in a crontab file represents a job in the syntax `* * * * * <command to execute>`. There are five fields which represent the time to execute a command, followed by a shell command itself.
327
+
Each line in a crontab file represents a job in the syntax `* * * * * <command to execute>`. There are five fields that represent the time to execute a command, followed by a shell command itself.
328
328
329
329
330
330
### Understanding A Cron Expression
@@ -339,16 +339,16 @@ In our case, we want to run `flask send-newsletter-email week1`. As a command li
339
339
340
340
-**Current directory**: We need to `cd` into the project's specific directory in the cronjob, by specifying its absolute path.
341
341
-**Environment variables**: Flask uses the `.env` and the `.flaskenv` files to automatically access an application's environment variables
342
-
-**Virtual environment**: Because it is a flask command, it is best to activate a virtual envronment in the process, or else, run a Python executable located inside the virtualenv directory.
343
-
-**Logging**: It is best to ensure that by sending the output to a logfile.
342
+
-**Virtual environment**: Because it is a flask command, it is best to activate a virtual environment in the process, or else, run a Python executable located inside the virtualenv directory.
343
+
-**Logging**: It is best to ensure that by sending the output to a log file.
344
344
345
345
To configure the `flask send-newsletter-email week1` command as a cron service, I can schedule week 1 emails to be sent out once every minute as follows:
346
346
347
347
```python
348
348
***** cd /home/harry/newsletter_app && venv/bin/flask send-newsletter-email week1 >> logs/scheduled_email.log 2>&1
349
349
```
350
350
351
-
I have used `&&` to include multiple commands in a single line. I have began by navigating into the directory containing the project. Instead of activating a virtual environment, I decided to locate the `flask` command inside the `venv/bin/` subdirectory which achieves the same effect as activating the environment.
351
+
I have used `&&` to include multiple commands in a single line. I began by navigating into the directory containing the project. Instead of activating a virtual environment, I decided to locate the `flask` command inside the `venv/bin/` subdirectory which achieves the same effect as activating the environment.
352
352
353
353
The `flask` command is immediately followed by my custom CLI commands. As soon as that is executed, the output is redirected and appended to the file `scheduled_email.log` for logging purposes. This helps to know if the job was done successfully or if there was an error. Otherwise, it would be very difficult to know what happened, especially in the event there is an unexpected error.
354
354
@@ -365,7 +365,7 @@ Once this job is executed, there will be a new file inside the `logs` sub-folder
365
365
| 5 4 * * * command | Run the command daily at 4.05 am |
366
366
| 5 16 * * * command | Run the command daily at 4.05 pm |
367
367
| 5 4 * * 2 command | Run the command every Tuesday at 4.05 am |
368
-
| 5 4 * * 1-5 command | Run the command every weekday at 4.05 am except the weekends |
368
+
| 5 4 * * 1-5 command | Run the command every weekday at 4.05 am except the weekends |
369
369
| 0-59/2 * * * command | Run the command daily every even minute of the hour |
370
370
| 1-59/2 * * * command | Run the command daily every odd minute of the hour |
0 commit comments