├── .gitignore ├── LICENSE ├── README.md ├── requirements.txt ├── test_thumbnail_maker.py └── thumbnail_maker.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .vscode 3 | .cache 4 | __pycache__ 5 | incoming 6 | outgoing 7 | staging 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tim Ojo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Concurrency Getting Started 2 | 3 | Code for Python Concurrency Getting Started Course on Pluralsight. 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Pillow 2 | pytest 3 | -------------------------------------------------------------------------------- /test_thumbnail_maker.py: -------------------------------------------------------------------------------- 1 | from thumbnail_maker import ThumbnailMakerService 2 | 3 | IMG_URLS = \ 4 | ['https://dl.dropboxusercontent.com/s/2fu69d8lfesbhru/pexels-photo-48603.jpeg', 5 | 'https://dl.dropboxusercontent.com/s/zch88m6sb8a7bm1/pexels-photo-134392.jpeg', 6 | 'https://dl.dropboxusercontent.com/s/lsr6dxw5m2ep5qt/pexels-photo-135130.jpeg', 7 | 'https://dl.dropboxusercontent.com/s/6xinfm0lcnbirb9/pexels-photo-167300.jpeg', 8 | 'https://dl.dropboxusercontent.com/s/2dp2hli32h9p0y6/pexels-photo-167921.jpeg', 9 | 'https://dl.dropboxusercontent.com/s/fjb1m3grcrceqo2/pexels-photo-173125.jpeg', 10 | 'https://dl.dropboxusercontent.com/s/56u8p4oplagc4bp/pexels-photo-185934.jpeg', 11 | 'https://dl.dropboxusercontent.com/s/2s1x7wz4sdvxssr/pexels-photo-192454.jpeg', 12 | 'https://dl.dropboxusercontent.com/s/1gjphqnllzm10hh/pexels-photo-193038.jpeg', 13 | 'https://dl.dropboxusercontent.com/s/pcjz40c8pxpy057/pexels-photo-193043.jpeg', 14 | 'https://dl.dropboxusercontent.com/s/hokdfk7y8zmwe96/pexels-photo-207962.jpeg', 15 | 'https://dl.dropboxusercontent.com/s/k2tk2co7r18juy7/pexels-photo-247917.jpeg', 16 | 'https://dl.dropboxusercontent.com/s/m4xjekvqk4rksbx/pexels-photo-247932.jpeg', 17 | 'https://dl.dropboxusercontent.com/s/znmswtwhcdbpc10/pexels-photo-265186.jpeg', 18 | 'https://dl.dropboxusercontent.com/s/jgb6n4esquhh4gu/pexels-photo-302899.jpeg', 19 | 'https://dl.dropboxusercontent.com/s/rjuggi2ubc1b3bk/pexels-photo-317156.jpeg', 20 | 'https://dl.dropboxusercontent.com/s/cpaog2nwplilrz9/pexels-photo-317383.jpeg', 21 | 'https://dl.dropboxusercontent.com/s/16x2b6ruk18gji5/pexels-photo-320007.jpeg', 22 | 'https://dl.dropboxusercontent.com/s/xqzqzjkcwl52en0/pexels-photo-322207.jpeg', 23 | 'https://dl.dropboxusercontent.com/s/frclthpd7t8exma/pexels-photo-323503.jpeg', 24 | 'https://dl.dropboxusercontent.com/s/7ixez07vnc3jeyg/pexels-photo-324030.jpeg', 25 | 'https://dl.dropboxusercontent.com/s/1xlgrfy861nyhox/pexels-photo-324655.jpeg', 26 | 'https://dl.dropboxusercontent.com/s/v1b03d940lop05d/pexels-photo-324658.jpeg', 27 | 'https://dl.dropboxusercontent.com/s/ehrm5clkucbhvi4/pexels-photo-325520.jpeg', 28 | 'https://dl.dropboxusercontent.com/s/l7ga4ea98hfl49b/pexels-photo-333529.jpeg', 29 | 'https://dl.dropboxusercontent.com/s/rleff9tx000k19j/pexels-photo-341520.jpeg' 30 | ] 31 | 32 | def test_thumbnail_maker(): 33 | tn_maker = ThumbnailMakerService() 34 | tn_maker.make_thumbnails(IMG_URLS) 35 | -------------------------------------------------------------------------------- /thumbnail_maker.py: -------------------------------------------------------------------------------- 1 | # thumbnail_maker.py 2 | import time 3 | import os 4 | import logging 5 | from urllib.parse import urlparse 6 | from urllib.request import urlretrieve 7 | 8 | import PIL 9 | from PIL import Image 10 | 11 | logging.basicConfig(filename='logfile.log', level=logging.DEBUG) 12 | 13 | class ThumbnailMakerService(object): 14 | def __init__(self, home_dir='.'): 15 | self.home_dir = home_dir 16 | self.input_dir = self.home_dir + os.path.sep + 'incoming' 17 | self.output_dir = self.home_dir + os.path.sep + 'outgoing' 18 | 19 | def download_images(self, img_url_list): 20 | # validate inputs 21 | if not img_url_list: 22 | return 23 | os.makedirs(self.input_dir, exist_ok=True) 24 | 25 | logging.info("beginning image downloads") 26 | 27 | start = time.perf_counter() 28 | for url in img_url_list: 29 | # download each image and save to the input dir 30 | img_filename = urlparse(url).path.split('/')[-1] 31 | urlretrieve(url, self.input_dir + os.path.sep + img_filename) 32 | end = time.perf_counter() 33 | 34 | logging.info("downloaded {} images in {} seconds".format(len(img_url_list), end - start)) 35 | 36 | def perform_resizing(self): 37 | # validate inputs 38 | if not os.listdir(self.input_dir): 39 | return 40 | os.makedirs(self.output_dir, exist_ok=True) 41 | 42 | logging.info("beginning image resizing") 43 | target_sizes = [32, 64, 200] 44 | num_images = len(os.listdir(self.input_dir)) 45 | 46 | start = time.perf_counter() 47 | for filename in os.listdir(self.input_dir): 48 | orig_img = Image.open(self.input_dir + os.path.sep + filename) 49 | for basewidth in target_sizes: 50 | img = orig_img 51 | # calculate target height of the resized image to maintain the aspect ratio 52 | wpercent = (basewidth / float(img.size[0])) 53 | hsize = int((float(img.size[1]) * float(wpercent))) 54 | # perform resizing 55 | img = img.resize((basewidth, hsize), PIL.Image.LANCZOS) 56 | 57 | # save the resized image to the output dir with a modified file name 58 | new_filename = os.path.splitext(filename)[0] + \ 59 | '_' + str(basewidth) + os.path.splitext(filename)[1] 60 | img.save(self.output_dir + os.path.sep + new_filename) 61 | 62 | os.remove(self.input_dir + os.path.sep + filename) 63 | end = time.perf_counter() 64 | 65 | logging.info("created {} thumbnails in {} seconds".format(num_images, end - start)) 66 | 67 | def make_thumbnails(self, img_url_list): 68 | logging.info("START make_thumbnails") 69 | start = time.perf_counter() 70 | 71 | self.download_images(img_url_list) 72 | self.perform_resizing() 73 | 74 | end = time.perf_counter() 75 | logging.info("END make_thumbnails in {} seconds".format(end - start)) 76 | --------------------------------------------------------------------------------