Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,39 @@ This repository accompanies [*Practical Docker with Python*](https://www.apress.

Download the files as a zip using the green button, or clone the repository to your machine using Git.

The source code is available chapter wise and is available in [/source-code](/source-code) directory. The zip files corresponding to each filename referenced in the exercise. The structure is as below

```
── chapter-4
│   ├── exercise-1
│   │   ├── docker-hello-world
│   ├── exercise-2
│   │   ├── docker-multi-stage
│   └── exercise-3
│   └── docker-subreddit-fetcher
├── chapter-5
│   ├── exercise-1
│   │   ├── docker-volume-bind-mount
│   └── exercise-2
│   ├── docker-subreddit-fetcher-volume
├── chapter-6
│   └── exercise-1
│   ├── docker-subreddit-fetcher-network
└── chapter-7
├── exercise-1
│   └── docker-compose-adminer
└── exercise-2
└── subreddit-fetcher-compose
```

## Releases

Release v1.0 corresponds to the code in the published book, without corrections or updates.

v1.1

- Some fixes to clean up the code structure and correct bugs as reported

## Contributions

See the file Contributing.md for more information on how you can contribute to this repository.
See the file Contributing.md for more information on how you can contribute to this repository.
1 change: 0 additions & 1 deletion docker subreddit fetcher/README.md

This file was deleted.

Binary file removed multistage-exercise/.DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions source-code/chapter-4/exercise-1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### README

This exercise contains the source code for the first exercise of chapter 4. At the start of the chapter, we introduced a simple Dockerfile that did not build due to syntax errors. Here, you’ll fix the Dockerfile and add some of the instructions that you learned about in this chapter.
3 changes: 3 additions & 0 deletions source-code/chapter-4/exercise-2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### README

This exercise contains the source code for the second exercise of chapter 4. In this exercise, you will build two Docker images, the first one using a standard build process using python:3 as the base image.
3 changes: 3 additions & 0 deletions source-code/chapter-4/exercise-3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### README

This exercise contains the source code for the third exercise of chapter 4. In this exercise, we’ll try writing the Dockerfile for the project.
3 changes: 3 additions & 0 deletions source-code/chapter-5/exercise-1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### README

This exercise contains the source code for the first exercise of chapter 5. In this exercise, we build an nginx Docker image with a Docker volume attached, which contains a custom nginx configuration. Toward the second part of the exercise, we will attach a bind mount and a volume containing a static web page and a custom nginx configuration. The intent of the exercise is help the readers understand how to leverage volumes and bind mounts to make local development easy.
5 changes: 5 additions & 0 deletions source-code/chapter-5/exercise-2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### README

This exercise contains the source code for the second exercise of chapter 5. In the previous chapters’ exercises, we wrote a Dockerfile for our project. However, as you might have noticed, killing the container would reset the state and we need to customize our bot all over again.

For this exercise, we will be working on a slightly modified codebase that has support for saving the preferences to a SQLite DB. We would use Docker Volumes to persist the database across containers.
5 changes: 5 additions & 0 deletions source-code/chapter-6/exercise-1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### README

This exercise contains the source code for the first exercise of chapter 6. In the previous chapter exercises, we wrote a Dockerfile for this project and built the container. We then used Docker Volumes to persist the database across containers. In this exercise, you will modify the project so that the data, instead of saving to a SQLite database, persists to a MySQL database.

You will then create a custom bridge network to connect the project container and the MySQL container.
3 changes: 3 additions & 0 deletions source-code/chapter-7/exercise-1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### README

This exercise contains the source code for the first exercise of chapter 7. In this exercise, we will build a multi-container application consisting of a container for the MySQL database and another container for Adminer, a popular Web UI for MySQL. Since we already have prebuilt images for MySQL and Adminer, we won’t have to build them.
5 changes: 5 additions & 0 deletions source-code/chapter-7/exercise-2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### README

This exercise contains the source code for the second exercise of chapter 7. In the previous chapter’s exercises, we wrote a Dockerfile for our project. Later, we added volumes and the data was persisted to SQLite. In this exercise, we change the project to use MySQL instead of SQLite.

For this exercise, we will be working on a slightly modified codebase, which has support for saving the preferences to a SQLite DB. We would use Docker Volumes to persist the database across containers.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM python:3-alpine

RUN apk add gcc musl-dev python3-dev libffi-dev openssl-dev
COPY * /apps/subredditfetcher/
WORKDIR /apps/subredditfetcher/

VOLUME [ "/apps/subredditfetcher" ]
RUN ["pip", "install", "-r", "requirements.txt"]
# RUN ["python", "one_time.py"]

ENV NBT_ACCESS_TOKEN="495637361:AAHIhiDTX1UeX17KJy0-FsMZEqEtCFYfcP8"

CMD ["python", "newsbot.py"]
22 changes: 22 additions & 0 deletions source-code/chapter-7/exercise-2/subreddit-fetcher-compose/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) 2015 Sathya

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
__author__ = 'Sathyajith'

import os
ERR_NO_SOURCE = 'No sources defined! Set a source using /source list, of, sub, reddits'
skip_list = []
sources_dict = {}

BOT_KEY = os.environ['NBT_ACCESS_TOKEN']
API_BASE = 'https://api.telegram.org/bot'
UPDATE_PERIOD = 6
FALSE_RESPONSE = {"ok": False}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
version: '3'
services:
app:
build: .
depends_on:
- mysql
restart: "on-failure"
volumes:
- "appdata:/apps/subredditfetcher"
mysql:
image: mysql
volumes:
- "dbdata:/var/lib/mysql"
environment:
- MYSQL_ROOT_PASSWORD=dontusethisinprod
adminer:
image: adminer
ports:
- "8080:8080"

volumes:
dbdata:
appdata:
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
865610176
22 changes: 22 additions & 0 deletions source-code/chapter-7/exercise-2/subreddit-fetcher-compose/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from flask import Flask
from newsbot import *
from states import States

bot = Flask(__name__)


@bot.route('/index')
def index():
return 'Thou shalt not pass!'


@bot.route('/telegram-update', methods=['POST'])
def telegram_update():
handle_incoming_messages(States.last_updated_id)


if __name__ == '__main__':
States.last_updated_id = get_last_updated()
bot.run()


Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from peewee import *
# db = SqliteDatabase('newsbot.db')
db = MySQLDatabase(host="mysql", port=3306, user="root", password="dontusethisinprod", database="newsbot")
class BaseModel(Model):
class Meta:
database = db

class Source(BaseModel):
person_id = PrimaryKeyField()
fetch_from = CharField()


class Request(BaseModel):
id = PrimaryKeyField()
from_id = IntegerField(index=True)
request_text = CharField()
received_time = DateTimeField()


class Message(BaseModel):
id = PrimaryKeyField()
sent_to_id = IntegerField(index=True)
sent_message = CharField()
sent_time = DateTimeField()
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from states import States, log
from telegram import handle_incoming_messages
from models import *
from time import sleep

import sys
import pymysql


def get_last_updated():
try:
with open('last_updated.txt', 'r') as f:
try:
last_updated = int(f.read())
except ValueError:
last_updated = 0
f.close()
except FileNotFoundError:
last_updated = 0
log.debug('Last updated id: {0}'.format(last_updated))
return last_updated

if __name__ == '__main__':
log.info('Starting up')
log.info('Waiting for 60 seconds for db to come up')
sleep(60)

log.info('Checking on dbs')
try:
db.connect()
except OperationalError as o:
print("Could not connect to db, please check db parameters")
sys.exit(-1)
except InternalError as e:
# 1049 is MySQL error code for db doesn't exist - so we create it.
db_connection = pymysql.connect(host='mysql', user= 'root', password='dontusethisinprod')
db_connection.cursor().execute('CREATE DATABASE newsbot')
db_connection.close()
db.create_tables([Source, Request, Message], True)

try:
States.last_updated = get_last_updated()
while True:
handle_incoming_messages(States.last_updated)
except KeyboardInterrupt:
log.info('Received KeybInterrupt, exiting')
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

from models import *


def create_tables():
db.connect()
db.create_tables([Source, Request, Message], True)
db.close()

if __name__ == '__main__':
create_tables()
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import praw
from states import log

__author__ = 'Sathyajith'


def summarize(url):
log.info('Not yet implemented!')
return url


def get_latest_news(sub_reddits):
log.debug('Fetching news from reddit')
r = praw.Reddit(user_agent='SubReddit Newsfetcher Bot',
client_id='ralalsYuEJXKDg',
client_secret="16DD-6O7VVaYVMlkUPZWLhdluhU")
r.read_only = True

# Can change the subreddit or add more.
sub_reddits = clean_up_subreddits(sub_reddits)
log.info('Fetching subreddits: {0}'.format(sub_reddits))
submissions = r.subreddit(sub_reddits).hot(limit=5)
submission_content = ''
try:
for post in submissions:
submission_content += summarize(post.title + ' - ' + post.url) + '\n\n'
except praw.errors.Forbidden:
log.debug('subreddit {0} is private'.format(sub_reddits))
submission_content = "Sorry couldn't fetch; subreddit is private"
except praw.errors.InvalidSubreddit:
log.debug('Subreddit {} is invalid or doesn''t exist.'.format(sub_reddits))
submission_content = "Sorry couldn't fetch; subreddit doesn't seem to exist"
return submission_content


def clean_up_subreddits(sub_reddits):
log.debug('Got subreddits to clean: {0}'.format(sub_reddits))
return sub_reddits.strip().replace(" ", "").replace(',', '+')
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
praw
peewee==2.10.2
PyMySQL
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import logging


class States(object):
last_updated_id = ''

logging.basicConfig(level=logging.INFO,
format='%(levelname)s: %(asctime)s - %(funcName)s - %(message)s')

log = logging.getLogger('nbt')
Loading