├── .github
└── CODE_OF_CONDUCT.md
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── docs
├── feedback.png
├── names.py
└── requirements.txt
└── problems
├── data_science
├── Analysis-Key.ipynb
├── Analysis-Workshop.ipynb
├── Introduction-to-Text-Analysis-with-sklearn-preview.ipynb
├── Introduction_to_Text_Analysis_with_sklearn.ipynb
├── battery_life
│ └── README.md
├── chipmunks
│ ├── chipmunk.csv
│ ├── data_science_project_night_4_18_19.ipynb
│ ├── logo@3x.png
│ └── requirements.txt
├── diversif.ai.ipynb
├── github_jobs_api
│ ├── README.md
│ ├── requirements.txt
│ └── upsetplot-example.png
├── home_credit_default_risk
│ ├── Pipfile
│ ├── Pipfile.lock
│ ├── README.md
│ ├── default_risk.ipynb
│ ├── default_risk_column_descriptions.csv
│ ├── default_risk_test_data.csv
│ ├── default_risk_train_data.csv
│ └── perfect_deliverable.csv
├── requirements.txt
├── talks.csv
├── team_project.ipynb
├── text-analysis.jpg
└── trackcoder.ipynb
├── py101
├── make_a_game
│ ├── Pipfile
│ ├── Pipfile.lock
│ ├── README.md
│ ├── lib
│ │ ├── __init__.py
│ │ └── player.py
│ ├── run.py
│ └── tests
│ │ ├── __init__.py
│ │ └── test_player.py
├── python_koans
│ └── README.md
├── python_team_project
│ ├── README.md
│ ├── app.py
│ └── requirements.txt
├── testing
│ ├── EnableTravisCI.png
│ ├── Makefile
│ ├── Pipfile
│ ├── Pipfile.lock
│ ├── README.md
│ ├── pytest.ini
│ ├── team_organizer.py
│ ├── test_team_organizer.py
│ └── travis-build-img.png
└── trackcoder
│ ├── Makefile
│ ├── Pipfile
│ ├── Pipfile.lock
│ ├── README.md
│ ├── app.py
│ ├── csv.png
│ ├── project.png
│ └── to_do_list.db
└── webdev
├── django_pn_tracker
├── .dockerignore
├── .gitignore
├── README.md
├── db.sqlite3
├── django_pn_tracker
│ ├── __init__.py
│ ├── apps
│ │ ├── __init__.py
│ │ └── challenges
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── apps.py
│ │ │ ├── forms.py
│ │ │ ├── migrations
│ │ │ ├── 0001_initial.py
│ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── templates
│ │ │ └── challenges
│ │ │ │ ├── delete.html
│ │ │ │ ├── edit.html
│ │ │ │ └── list.html
│ │ │ ├── tests.py
│ │ │ ├── urls.py
│ │ │ └── views.py
│ ├── settings.py
│ ├── static
│ │ ├── css
│ │ │ ├── bootstrap.min.css
│ │ │ └── master.css
│ │ └── js
│ │ │ ├── bootstrap.min.js
│ │ │ └── main.js
│ ├── templates
│ │ ├── base.html
│ │ └── index.html
│ ├── urls.py
│ ├── views.py
│ └── wsgi.py
├── manage.py
├── requirements.txt
└── setup.cfg
├── django_rest_framework_api
├── Pipfile
├── Pipfile.lock
└── README.md
├── flask_collage
├── README.md
└── solutions
│ ├── dormamu_bargain
│ └── README.md
│ └── testflask.py
├── flask_exchange_rates
└── README.md
├── flask_team_project
├── README.md
├── app.py
├── requirements.txt
└── templates
│ ├── rsvps.html
│ └── teams.html
└── flask_trackcoder
├── README.md
├── app.py
├── dashboard.gif
├── requirements.txt
├── static
└── styles
│ └── dashboard.css
└── templates
└── tasks.html
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | [Here](http://www.chipy.org/pages/conduct/) is our code of conduct.
3 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .coverage.*
42 | .cache
43 | nosetests.xml
44 | coverage.xml
45 | *,cover
46 | .hypothesis/
47 |
48 | # Translations
49 | *.mo
50 | *.pot
51 |
52 | # Django stuff:
53 | *.log
54 | local_settings.py
55 |
56 | # Flask stuff:
57 | instance/
58 | .webassets-cache
59 |
60 | # Scrapy stuff:
61 | .scrapy
62 |
63 | # Sphinx documentation
64 | docs/_build/
65 |
66 | # PyBuilder
67 | target/
68 |
69 | # IPython Notebook
70 | .ipynb_checkpoints
71 |
72 | # pyenv
73 | .python-version
74 |
75 | # celery beat schedule file
76 | celerybeat-schedule
77 |
78 | # dotenv
79 | .env
80 |
81 | # virtualenv
82 | venv/
83 | ENV/
84 |
85 | # Spyder project settings
86 | .spyderproject
87 |
88 | # Rope project settings
89 | .ropeproject
90 |
91 | # mypy
92 | .mypy_cache/
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "3.6"
4 | # add other problem directories
5 | env:
6 | - TEST_DIR=problems/py101/testing
7 |
8 | script:
9 | - cd $TEST_DIR && make
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 1. Python Project Night - Hands on Python programming for Chicago Pythonistas
2 |
3 | Welcome to the Python Projects Night organized by Chicago Python User Group or ChiPy.
4 | Here you will find python challenges that we solve together as a groups every third
5 | Thursday of the month at 6:30 pm at Braintree's Chicago Office. This repository has the
6 | collection of the Challenge problems that we solve at Project Nights.
7 |
8 | [Talk to us on Slack](https://chipy.slack.com/messages/C4SRS5G3B/details/)
9 |
10 | ## 1.1. Code of Conduct
11 |
12 | [Here](http://www.chipy.org/pages/conduct/) is our code of conduct.
13 |
14 | ## 1.2. Contributors
15 |
16 | [](https://sourcerer.io/fame/tathagata/chicagopython/CodingWorkshops/links/0)[](https://sourcerer.io/fame/tathagata/chicagopython/CodingWorkshops/links/1)[](https://sourcerer.io/fame/tathagata/chicagopython/CodingWorkshops/links/2)[](https://sourcerer.io/fame/tathagata/chicagopython/CodingWorkshops/links/3)[](https://sourcerer.io/fame/tathagata/chicagopython/CodingWorkshops/links/4)[](https://sourcerer.io/fame/tathagata/chicagopython/CodingWorkshops/links/5)[](https://sourcerer.io/fame/tathagata/chicagopython/CodingWorkshops/links/6)[](https://sourcerer.io/fame/tathagata/chicagopython/CodingWorkshops/links/7)
17 |
18 |
19 | If you have found a bug, typo or want to help out, please look at the contributing guidelines.
20 |
21 | ## 1.3. Challenge Problems
22 |
23 | Challenge problems are fun, hands-on coding exercises covering a variety of topics -- such as pure problem solving, web development, and data science. The participants of Project Night are assigned to teams of four, and then solve the problem together in an hour. Teams are designed to have diverse experience levels, giving team members equal opportunity to learn and share ideas.
24 |
25 | ### 1.3.1. What you need
26 |
27 | - Wi-fi powered laptop, power chord
28 | - Python: 3.5 (We can not guarantee help if you are using Python 2.7)
29 | - Text editor: Atom, Sublime Text, Visual Studio Code
30 | - OS: GNU/Linux. OS X, Windows
31 |
32 | ## 1.4. Previous Projects
33 |
34 | Here is a list of projects we have solved previosuly. These are great exercises
35 | if you are planning to get level up your skills. Try them out and if you need any
36 | help [ask us on slack](https://chipy.slack.com/messages/C4SRS5G3B/details/)
37 |
38 | ### 1.4.1. Python101
39 |
40 | #### 1.4.1.1. Python Koans
41 |
42 | For this exercise we will learn the Zen of Python using Test Driven Development.
43 | Python Koans is a suite of broken tests, which are written against Python code that
44 | demonstrate how to write idiomatic python.
45 | Your job is to fix the broken tests by filling in the missing parts of the code.
46 |
47 | [Solve it!](problems/py101/python_koans)
48 |
49 | #### 1.4.1.2. Python Team Projects
50 |
51 | The organizers of Project Nights need your help! Grouping attendees for Project Night team project is a manual task. Why do it manually, when we can automate it? We open up the problem to you.
52 |
53 | [Solve it!](problems/py101/python_team_project)
54 |
55 | #### 1.4.1.3. Intro to PyTest and Travis-CI
56 |
57 | This is an introduction to testing using pytest that uses the same problem as above of grouping project night attendees into teams of 4.
58 |
59 | [Solve it!](problems/py101/testing)
60 |
61 | ### 1.4.2. WebDev
62 |
63 | #### 1.4.2.1. Flask Team Projects
64 |
65 | Build a web app for to group the Python project night attendees.
66 |
67 | [Solve it!](problems/webdev/flask_team_project)
68 |
69 | #### 1.4.2.2. Flask Collage
70 |
71 | Build a small web app using Flask which accepts the meetup.com event id for tonight
72 | as a parameter and would fetch the profile pictures of all the attendees to create a
73 | collage.
74 |
75 | [Solve it!](problems/webdev/flask_collage)
76 |
77 | ### 1.4.3. Data Science
78 |
79 | #### 1.4.3.1. Data Analysis With Pandas
80 |
81 | Pandas is the defacto package for data analysis in Python. In this project, we are going to use the basics of pandas to analyze the interests of project Night's attendees. What can we learn about the Python Project Night's attendees by mining their meetup.com
82 | profile data?
83 |
84 | [Solve it!](problems/data_science/Analysis-Workshop.ipynb)
85 |
86 | #### 1.4.3.2. Diversif.ai
87 |
88 | Diversity in tech communities has been a widely addressed topic. As one of the most active tech community in the world, in this exercise we would try to measure some aspects of diversity in tech community. We will use image recognition on the meetup.com profile pictures of the members of ChiPy user group and determine determine how diverse our attendees are. Then we will compare the same with other tech groups in the city and around the world.
89 |
90 | [Solve it!](problems/data_science/diversif.ai.ipynb)
91 |
92 | #### 1.4.3.3. Introduction to Text Analysis with sklearn
93 |
94 | This is a gentle introduction to text analysis with sklearn. We build a recommnedation system that predicts
95 | Pycon conference talks based on previous history of Pycon talks.
96 |
--------------------------------------------------------------------------------
/docs/feedback.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/docs/feedback.png
--------------------------------------------------------------------------------
/docs/names.py:
--------------------------------------------------------------------------------
1 | from nameparser import HumanName
2 | import pandas as pd
3 | import numpy as np
4 |
5 | import time
6 |
7 | '''
8 | download data from meetup.com
9 | convert it to xlsx
10 | #TODO - use meetup.com api if we continue to use the meetup.com's garbage rsvp system
11 | '''
12 |
13 | df = pd.read_excel('input.xlsx')
14 | df.rename(columns={'Please enter your full name as it appears on your ID. This is required for security reasons for access to the venue.':'FullName'}, inplace=True)
15 |
16 | df1 = df.replace(np.nan, '', regex=True)
17 | res = pd.DataFrame()
18 |
19 | res['MeetupName'] = df1['Name']
20 | res['LastName'] = df1.apply(lambda x: HumanName(x['FullName']).last if x['FullName'] else '', axis=1)
21 | res['FirstName'] = df1.apply(lambda x: HumanName(x['FullName']).first if x['FullName'] else '', axis=1)
22 | res['Middle'] = df1.apply(lambda x: HumanName(x['FullName']).middle if x['FullName'] else '', axis=1)
23 | res['Suffix'] = df1.apply(lambda x: HumanName(x['FullName']).suffix if x['FullName'] else '', axis=1)
24 |
25 | pd.options.display.max_rows = 999
26 | r=res.reindex(columns=['MeetupName','FirstName','Middle','LastName','Suffix'])
27 |
28 | timestr = time.strftime("%Y%m%d-%H%M%S")
29 | writer = pd.ExcelWriter(f'output_{timestr}.xlsx')
30 | r.to_excel(writer,'Sheet1')
31 | writer.save()
32 |
33 | '''
34 | email to:
35 | chicagooffice AT braintreepayments DOT com
36 | community AT getbraintree DOT com
37 | ray
38 | zax
39 | '''
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | nameparser
2 | pandas
--------------------------------------------------------------------------------
/problems/data_science/Analysis-Workshop.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "### Data Analysis using Pandas\n",
8 | "Pandas has become the defacto package for data analysis. In this workshop, we are going to use the basics of pandas to analyze the interests of today's group. We are going to use meetup.com's api and fetch the list of interests that are listed in each of our meetup.com profile. We will compute which interests are common, which are uncommon, and find out which of the two members have most similar interests. Lets get started by importing the essentials.\n",
9 | "\n",
10 | "You would need meetup.com's python api and pandas installed."
11 | ]
12 | },
13 | {
14 | "cell_type": "code",
15 | "execution_count": null,
16 | "metadata": {
17 | "collapsed": true
18 | },
19 | "outputs": [],
20 | "source": [
21 | "import meetup.api\n",
22 | "import pandas as pd\n",
23 | "from IPython.display import Image, display, HTML\n",
24 | "from itertools import combinations"
25 | ]
26 | },
27 | {
28 | "cell_type": "markdown",
29 | "metadata": {},
30 | "source": [
31 | "Next we need your meetup.com API. You will find it https://secure.meetup.com/meetup_api/key/ \n",
32 | "Also we need today's event id. The event id created under Chicago Pythonistas is **233460758** and that under Chicago Python user group is **236205125**. Use the one that has the higher number of RSVPs so that you get more data points. As an additional exercise, you might go for merging the two sets of RSVPs - but that's not needed for the workshop."
33 | ]
34 | },
35 | {
36 | "cell_type": "code",
37 | "execution_count": null,
38 | "metadata": {
39 | "collapsed": true
40 | },
41 | "outputs": [],
42 | "source": [
43 | "API_KEY = ''\n",
44 | "event_id=''"
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "metadata": {},
50 | "source": [
51 | "The following function uses the api and loads the data into a pandas data frame. Note we are a bit sloppy both in style and how we load the data. In actual production code, we should add adequate logging with well-defined exceptions to indicate what's going wrong."
52 | ]
53 | },
54 | {
55 | "cell_type": "code",
56 | "execution_count": 114,
57 | "metadata": {
58 | "collapsed": false
59 | },
60 | "outputs": [],
61 | "source": [
62 | "def get_members(event_id):\n",
63 | " client = meetup.api.Client(API_KEY)\n",
64 | " rsvps=client.GetRsvps(event_id=event_id, urlname='_ChiPy_')\n",
65 | " member_id = ','.join([str(i['member']['member_id']) for i in rsvps.results])\n",
66 | " return client.GetMembers(member_id=member_id)\n",
67 | "\n",
68 | "def get_topics(members):\n",
69 | " topics = set()\n",
70 | " for member in members.results:\n",
71 | " try:\n",
72 | " for t in member['topics']:\n",
73 | " topics.add(t['name'])\n",
74 | " except:\n",
75 | " pass\n",
76 | "\n",
77 | " return list(topics)\n",
78 | "\n",
79 | "def df_topics(event_id):\n",
80 | " members = get_members(event_id=event_id)\n",
81 | " topics = get_topics(members)\n",
82 | " columns=['name','id','thumb_link'] + topics\n",
83 | " \n",
84 | " data = [] \n",
85 | " for member in members.results:\n",
86 | " topic_vector = [0]*len(topics)\n",
87 | " for topic in member['topics']:\n",
88 | " index = topics.index(topic['name']) \n",
89 | " topic_vector[index-1] = 1\n",
90 | " try:\n",
91 | " data.append([member['name'], member['id'], member['photo']['thumb_link']] + topic_vector)\n",
92 | " except:\n",
93 | " pass\n",
94 | " return pd.DataFrame(data=data, columns=columns)\n",
95 | " \n",
96 | " #df.to_csv('output.csv', sep=\";\")"
97 | ]
98 | },
99 | {
100 | "cell_type": "markdown",
101 | "metadata": {},
102 | "source": [
103 | "So you need to call the df_topics function with the event id and it would give you back a pandas dataframe containing basic information of a member and along with all possible interests. If the member has indicated interest, that column will have a one, if not then the column will have a zero."
104 | ]
105 | },
106 | {
107 | "cell_type": "markdown",
108 | "metadata": {},
109 | "source": [
110 | "### Load data from meetup.com into a dataframe by calling df_topics with the event id as parameter"
111 | ]
112 | },
113 | {
114 | "cell_type": "code",
115 | "execution_count": null,
116 | "metadata": {
117 | "collapsed": false
118 | },
119 | "outputs": [],
120 | "source": []
121 | },
122 | {
123 | "cell_type": "markdown",
124 | "metadata": {},
125 | "source": [
126 | "### What does the first and last 10 rows of the dataset look like?"
127 | ]
128 | },
129 | {
130 | "cell_type": "code",
131 | "execution_count": null,
132 | "metadata": {
133 | "collapsed": true
134 | },
135 | "outputs": [],
136 | "source": []
137 | },
138 | {
139 | "cell_type": "markdown",
140 | "metadata": {},
141 | "source": [
142 | "### What are the column names?"
143 | ]
144 | },
145 | {
146 | "cell_type": "code",
147 | "execution_count": null,
148 | "metadata": {
149 | "collapsed": true
150 | },
151 | "outputs": [],
152 | "source": []
153 | },
154 | {
155 | "cell_type": "markdown",
156 | "metadata": {},
157 | "source": [
158 | "### Additional Exercise: Can you merge the two data for two events into one data frame and remove the dups?"
159 | ]
160 | },
161 | {
162 | "cell_type": "code",
163 | "execution_count": null,
164 | "metadata": {
165 | "collapsed": true
166 | },
167 | "outputs": [],
168 | "source": []
169 | },
170 | {
171 | "cell_type": "markdown",
172 | "metadata": {},
173 | "source": [
174 | "### What are the top 10 most common interests of today’s attendees?"
175 | ]
176 | },
177 | {
178 | "cell_type": "code",
179 | "execution_count": null,
180 | "metadata": {
181 | "collapsed": false
182 | },
183 | "outputs": [],
184 | "source": []
185 | },
186 | {
187 | "cell_type": "markdown",
188 | "metadata": {},
189 | "source": [
190 | "### What is the third most popular and third least popular topic of interest? Are there ties?"
191 | ]
192 | },
193 | {
194 | "cell_type": "code",
195 | "execution_count": null,
196 | "metadata": {
197 | "collapsed": false
198 | },
199 | "outputs": [],
200 | "source": []
201 | },
202 | {
203 | "cell_type": "markdown",
204 | "metadata": {},
205 | "source": [
206 | "### Which members have the third most popular interest?"
207 | ]
208 | },
209 | {
210 | "cell_type": "code",
211 | "execution_count": null,
212 | "metadata": {
213 | "collapsed": false
214 | },
215 | "outputs": [],
216 | "source": []
217 | },
218 | {
219 | "cell_type": "markdown",
220 | "metadata": {},
221 | "source": [
222 | "### Which members have the third most popular interest?"
223 | ]
224 | },
225 | {
226 | "cell_type": "code",
227 | "execution_count": null,
228 | "metadata": {
229 | "collapsed": false
230 | },
231 | "outputs": [],
232 | "source": []
233 | },
234 | {
235 | "cell_type": "markdown",
236 | "metadata": {},
237 | "source": [
238 | "### Which memebers have the highest number of topics of interest?"
239 | ]
240 | },
241 | {
242 | "cell_type": "code",
243 | "execution_count": null,
244 | "metadata": {
245 | "collapsed": false,
246 | "scrolled": true
247 | },
248 | "outputs": [],
249 | "source": []
250 | },
251 | {
252 | "cell_type": "markdown",
253 | "metadata": {},
254 | "source": [
255 | "### What is the average number of topics of interest?"
256 | ]
257 | },
258 | {
259 | "cell_type": "code",
260 | "execution_count": null,
261 | "metadata": {
262 | "collapsed": false
263 | },
264 | "outputs": [],
265 | "source": []
266 | },
267 | {
268 | "cell_type": "markdown",
269 | "metadata": {},
270 | "source": [
271 | "### Which two members have the most common overlap of interests?"
272 | ]
273 | },
274 | {
275 | "cell_type": "code",
276 | "execution_count": null,
277 | "metadata": {
278 | "collapsed": false
279 | },
280 | "outputs": [],
281 | "source": []
282 | },
283 | {
284 | "cell_type": "markdown",
285 | "metadata": {},
286 | "source": [
287 | "### How many members are there who have no overlaps at all?"
288 | ]
289 | },
290 | {
291 | "cell_type": "code",
292 | "execution_count": null,
293 | "metadata": {
294 | "collapsed": true
295 | },
296 | "outputs": [],
297 | "source": []
298 | },
299 | {
300 | "cell_type": "markdown",
301 | "metadata": {},
302 | "source": [
303 | "### Given a member which other member(s) have the most common interests?"
304 | ]
305 | },
306 | {
307 | "cell_type": "code",
308 | "execution_count": null,
309 | "metadata": {
310 | "collapsed": true
311 | },
312 | "outputs": [],
313 | "source": []
314 | }
315 | ],
316 | "metadata": {
317 | "kernelspec": {
318 | "display_name": "Python 2",
319 | "language": "python",
320 | "name": "python2"
321 | },
322 | "language_info": {
323 | "codemirror_mode": {
324 | "name": "ipython",
325 | "version": 2
326 | },
327 | "file_extension": ".py",
328 | "mimetype": "text/x-python",
329 | "name": "python",
330 | "nbconvert_exporter": "python",
331 | "pygments_lexer": "ipython2",
332 | "version": "2.7.11"
333 | }
334 | },
335 | "nbformat": 4,
336 | "nbformat_minor": 0
337 | }
338 |
--------------------------------------------------------------------------------
/problems/data_science/battery_life/README.md:
--------------------------------------------------------------------------------
1 | # Predicting Battery Life - Challenge #1: Gathering Data
2 |
3 | Portable electronics such as mobile phones and laptops have become a near necessity in our daily lives; and those devices share one essential resource in common: battery life. Have you ever sat on the floor to be by a power outlet for your laptop or cell phone? How about delayed leaving for an event because you had to make sure your phone was charged? In a perfect world, we wouldn't have to worry about battery life, but in the absence of the miracle battery, users must rely on indicators of remaining battery life.
4 |
5 | While there's still ongoing research into the capacity of batteries over time, the question of what percentage of charge remains has largely been solved in our everyday electronics. Most operating systems offer a way to display the percentage of battery life remaining. However, features that predict time remaining on the battery have been notoriously inaccurate, to the point where such features have been removed or hidden by default. Wouldn't it be nice if we could accurately predict when our phone was going to "die?"
6 |
7 | **Your goal is going to be work toward that solution by gathering data to build a machine learning model predicting remaining battery life. You are tasked with determining what data we might want to collect for such a model, determining a strategy for ongoing collection of that data, actually collecting it, and organizing it into a form that will be usable to the machine-learning models of choice.** There are no right or wrong answer here, just things that are feasible and ultimately help drive better predictions.
8 |
9 | Before digging in, some background reading on how batteries work and what kinds of data/models can be useful is likely in order. Feel free to find your own resources, but here are a few:
10 | - Overview of research and features: https://arxiv.org/pdf/1801.04069.pdf
11 | - Battery Terminology: http://web.mit.edu/evt/summary_battery_specifications.pdf
12 | - Battery Discharge Formulas: https://planetcalc.com/2283/
13 |
14 | Once you're ready to collect data, you'll likely want to collect running process and/or system utilization data as at least part of the data you collect. Gathering such data can vary drastically by hardware and operating system. To get you started, here are a few options to make extracting the data easier:
15 | - The [psutil](https://psutil.readthedocs.io/en/latest/) library in python has cross-OS support, but only collects some such data.
16 | - On Windows, there's the [wmi](http://timgolden.me.uk/python/wmi/tutorial.html) library.
17 | - On most distributions of Linux and MacOS, the standard librarys' os, sys, and subprocess modules can actually get you rolling pretty quickly, once you track down where system logs are stored!
18 |
19 | The rest is up to you, but some questions you might want to consider:
20 | - When tracking battery/system data, how are you accounting for a device sometimes being plugged in?
21 | - How will you account for different battery types, device types, and operating systems?
22 | - Besides the obvious battery and system-related data, what features might help predict battery life?
23 | - How can you collect enough data from enough sources to successfully train a model?
24 |
--------------------------------------------------------------------------------
/problems/data_science/chipmunks/data_science_project_night_4_18_19.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "\n",
8 | "\n",
9 | "# Oh, no! We've had a data crash.\n",
10 | "As ChiPy leadership was preparing for [PyCon](https://us.pycon.org/2019/) at the end of this month, they found that the dataset on our infamous *ChiPy chipmunks* has disapeared. While they transition from Oracle to Postgres, the leadership team has enlisted your help as data scientists to analyze some salvaged chipmunk data. The PyCon organizers had a few questions about coding in Chicago, ChiPy, and chipmunks that need answers. We will get to those questions shortly, but first let's get to the data. If you haven't set up your environment, make sure to checkout the project night page on the [website](http://chicagopython.github.io/posts/chipy-chipmunks).\n",
11 | "\n",
12 | "\n",
13 | "## Reading in the Data\n",
14 | "The salvaged chipmunk dataset is `chipmunk.csv`. The wonderful [pandas](https://pandas.pydata.org/) library, built on [numpy](http://www.numpy.org/), will let the team read in the data. \n",
15 | "\n",
16 | "
ChiPy Check-in
\n",
17 | "\n",
18 | "Now is a good time to check in with the team. Is anyone familiar with `pandas` and `numpy`? Discuss with your team what these libraries are, what they allow data scientists to do, and then decide on what `pandas` function will read in our data."
19 | ]
20 | },
21 | {
22 | "cell_type": "code",
23 | "execution_count": null,
24 | "metadata": {},
25 | "outputs": [],
26 | "source": [
27 | "import numpy as np\n",
28 | "import pandas as pd\n",
29 | "import matplotlib.pyplot as plt\n",
30 | "import seaborn as sns\n",
31 | "%matplotlib inline"
32 | ]
33 | },
34 | {
35 | "cell_type": "code",
36 | "execution_count": null,
37 | "metadata": {},
38 | "outputs": [],
39 | "source": [
40 | "# Read in the data"
41 | ]
42 | },
43 | {
44 | "cell_type": "markdown",
45 | "metadata": {},
46 | "source": [
47 | "## Exploring the data\n",
48 | "We need to be familiar with our data before we can answer questions about ChiPy and our chipmunks. Let's start with some questions we would ask of *any* dataset:\n",
49 | "\n",
50 | "- How many rows are in this dataset? What does each row represent?\n",
51 | "- What does the data look like? Check the first 5 rows\n",
52 | "- Is there missing data? If so, how much is missing?\n",
53 | "- What columns are categorical?\n",
54 | "- What are the unique number of observations for each column?\n"
55 | ]
56 | },
57 | {
58 | "cell_type": "code",
59 | "execution_count": null,
60 | "metadata": {},
61 | "outputs": [],
62 | "source": [
63 | "## Check the number of rows"
64 | ]
65 | },
66 | {
67 | "cell_type": "code",
68 | "execution_count": null,
69 | "metadata": {},
70 | "outputs": [],
71 | "source": [
72 | "## See first 5 rows of data"
73 | ]
74 | },
75 | {
76 | "cell_type": "code",
77 | "execution_count": null,
78 | "metadata": {},
79 | "outputs": [],
80 | "source": [
81 | "## Check for missing data"
82 | ]
83 | },
84 | {
85 | "cell_type": "code",
86 | "execution_count": null,
87 | "metadata": {},
88 | "outputs": [],
89 | "source": [
90 | "## Check for categorical data and unique number of values\n"
91 | ]
92 | },
93 | {
94 | "cell_type": "markdown",
95 | "metadata": {},
96 | "source": [
97 | "## Was there missing data?\n",
98 | "\n",
99 | "We will keep exploring the data and start answering questions soon, but first let's address missing data (if there is any). What columns have missing data? What kind of data is missing?\n",
100 | "\n",
101 | "
ChiPy Check-in
\n",
102 | "\n",
103 | "This a great point for discussion. If there is missing data - why might it be missing? Discuss some possible reasons with your team and decide on a reason that makes sense.\n",
104 | "\n",
105 | "[Imputation](https://en.wikipedia.org/wiki/Imputation_(statistics)) is the process of replacing missing data with some estimated value. The process can be as complicated (or simple) as you would like it to be! Given the possible reason for our missing data, what is an acceptable imputation?\n",
106 | "\n",
107 | "Impute any missing data in your dataset and note what assumptions you made as a team. If you are not sure how to replace data in `pandas`, feel free to use google like a proper data scientist."
108 | ]
109 | },
110 | {
111 | "cell_type": "code",
112 | "execution_count": null,
113 | "metadata": {},
114 | "outputs": [],
115 | "source": [
116 | "# Replace any missing data here"
117 | ]
118 | },
119 | {
120 | "cell_type": "code",
121 | "execution_count": null,
122 | "metadata": {},
123 | "outputs": [],
124 | "source": [
125 | "# Check your data for missing values to see if it worked!"
126 | ]
127 | },
128 | {
129 | "cell_type": "markdown",
130 | "metadata": {},
131 | "source": [
132 | "# Stakeholder Questions\n",
133 | "## Question #1\n",
134 | "\n",
135 | "The great folks at PyCon want to know all about ChiPy and our chipmunks. They have heard that **ChiPy is an inclusive and open community**. Can we support that claim with our data? Given that the `ChiPy` column takes a value of `1` for a ChiPy chipmunk and a value of `0` for chipmunks not in ChiPy, start to explore this question.\n",
136 | "\n",
137 | "Some ideas to get you started:\n",
138 | "- Are chipmunks of different species represented in ChiPy?\n",
139 | "- Are chipmunks of different sizes represented in ChiPy?\n",
140 | "- Are chipmunks of different careers represented in ChiPy?\n",
141 | "- Are spotted and not spotted chipmunks represented in ChiPy?\n",
142 | "\n",
143 | "
ChiPy Check-in
\n",
144 | "\n",
145 | "There are no right or wrong answers here, only well supported or poorly supported ones! Discuss as a group the aspects of the data you have looked at and if it constitutes enough evidence to justify an answer."
146 | ]
147 | },
148 | {
149 | "cell_type": "code",
150 | "execution_count": null,
151 | "metadata": {},
152 | "outputs": [],
153 | "source": [
154 | "## Exploration of species"
155 | ]
156 | },
157 | {
158 | "cell_type": "code",
159 | "execution_count": null,
160 | "metadata": {},
161 | "outputs": [],
162 | "source": [
163 | "## Exploration of size"
164 | ]
165 | },
166 | {
167 | "cell_type": "code",
168 | "execution_count": null,
169 | "metadata": {},
170 | "outputs": [],
171 | "source": [
172 | "## Exploration of careers"
173 | ]
174 | },
175 | {
176 | "cell_type": "code",
177 | "execution_count": null,
178 | "metadata": {},
179 | "outputs": [],
180 | "source": [
181 | "## Exploration of spotted vs non-spotted"
182 | ]
183 | },
184 | {
185 | "cell_type": "markdown",
186 | "metadata": {},
187 | "source": [
188 | "## Question #2\n",
189 | "The word on the street at PyCon is that chipmunks that live in Chicago enjoy coding more than those that don't. Is this not true? Given that the `chicago` column takes a value of `1` for chipmunks that live in Chicago and a value of `0` for chipmunks that do not, explore this question.\n",
190 | "\n",
191 | "- Visualize the distributions of `coding_enjoyment` for chipmunks that do and do not live in Chicago.\n",
192 | "- Come up with a way to test our question.\n",
193 | "\n",
194 | "
ChiPy Check-in
\n",
195 | "\n",
196 | "Coming up with a proper way to test stakeholder questions can be an artform as well as a science. We have imported a few statistical tests below that may (or may not) be appropriate for our question. First consider a way to frame our question as something to *disprove* (those familiar with jargon, let's construct a [null hypothesis](https://en.wikipedia.org/wiki/Null_hypothesis)) - then conduct a test that may disprove it. Reading the documentation for the imported tests below may prove to be very helpful!"
197 | ]
198 | },
199 | {
200 | "cell_type": "code",
201 | "execution_count": null,
202 | "metadata": {},
203 | "outputs": [],
204 | "source": [
205 | "from scipy.stats import ttest_ind, levene, chisquare"
206 | ]
207 | },
208 | {
209 | "cell_type": "code",
210 | "execution_count": null,
211 | "metadata": {},
212 | "outputs": [],
213 | "source": [
214 | "## Beautiful plot"
215 | ]
216 | },
217 | {
218 | "cell_type": "code",
219 | "execution_count": null,
220 | "metadata": {},
221 | "outputs": [],
222 | "source": [
223 | "## Statistical Test"
224 | ]
225 | },
226 | {
227 | "cell_type": "markdown",
228 | "metadata": {},
229 | "source": [
230 | "## Question #2, Continued\n",
231 | "\n",
232 | "We have now compared two groups of chipmunks - those that live in Chicago and those that do not - and have either rejected or failed to reject a null hypothesis. What values did the statistical test return and what do they mean? Can we be confident in our results? How confident?\n",
233 | "\n",
234 | "Regardless of our test results, what are the limitations of the test? One limitation is that we have information in our data that is *related* to being in Chicago and might also have an effect on enjoyment of coding. [Regression analysis](https://en.wikipedia.org/wiki/Regression_analysis) will allow us to examine the relationship between living in Chicago and enjoyment of coding while controlling for membership in ChiPy. Use the `statsmodels` package to regress `chicago` and `ChiPy` on `coding_enjoyment`. See [this example](https://www.statsmodels.org/stable/index.html) for assistance.\n",
235 | "\n",
236 | "
ChiPy Check-in
\n",
237 | "\n",
238 | "This regression model still has limitations, and there could be an entire project night on this task alone. What steps would need to be taken if we controlled for more characteristics of our data?\n",
239 | "\n",
240 | "This is also a good time to discuss what kind of information we are looking for in our regression model. What are coefficients and what do they mean? What is a p-value? Is it similar to a p-value from the statistical tests above?\n",
241 | "\n",
242 | "Lastly, modeling is fun, but don't forget the original question! Do chipmunks that live in Chicago enjoy coding more than those that don't?\n"
243 | ]
244 | },
245 | {
246 | "cell_type": "code",
247 | "execution_count": null,
248 | "metadata": {},
249 | "outputs": [],
250 | "source": [
251 | "import statsmodels.api as sm\n",
252 | "import statsmodels.formula.api as smf"
253 | ]
254 | },
255 | {
256 | "cell_type": "code",
257 | "execution_count": null,
258 | "metadata": {},
259 | "outputs": [],
260 | "source": [
261 | "# Regression model and summary"
262 | ]
263 | },
264 | {
265 | "cell_type": "markdown",
266 | "metadata": {},
267 | "source": [
268 | "## Question #3\n",
269 | "ChiPy leadership wants to send 20 lucky ChiPy chipmunks to cheer the lovely folks at PyCon. However, it's unlikely that the data recovery efforts will be able to recover who is/isn't a member of ChiPy! ChiPy leadership has asked us to develop a predictive model to identify members as part of the process to allocate the 20 free tickets.\n",
270 | "\n",
271 | "To do this we will:\n",
272 | "- Make a train/test split to evaluate our model\n",
273 | "- Scale our data\n",
274 | "- Fit several models\n",
275 | "- Decide on an evaluation metric\n",
276 | "- Select this best model\n",
277 | "\n",
278 | "
\n",
348 | "\n",
349 | "Having the proper evaluation metric is the most important process in predictive modeling. Below we have imported accuracy, precision, and recall. What are each of these metrics and when should they be used? Given that we want to give 20 PyCon tickets to only ChiPy chipmunks, which metric is most appropriate here? Black box evaluation methods like `classification_report` will not be helpful here given the constraint of only having 20 tickets. "
350 | ]
351 | },
352 | {
353 | "cell_type": "code",
354 | "execution_count": null,
355 | "metadata": {},
356 | "outputs": [],
357 | "source": [
358 | "from sklearn.metrics import precision_score, recall_score, accuracy_score, confusion_matrix"
359 | ]
360 | },
361 | {
362 | "cell_type": "code",
363 | "execution_count": null,
364 | "metadata": {},
365 | "outputs": [],
366 | "source": [
367 | "### Get predictions..."
368 | ]
369 | },
370 | {
371 | "cell_type": "code",
372 | "execution_count": null,
373 | "metadata": {},
374 | "outputs": [],
375 | "source": [
376 | "### Evaluate models, optimizing your predictions for 20 chipmunks!"
377 | ]
378 | },
379 | {
380 | "cell_type": "code",
381 | "execution_count": null,
382 | "metadata": {},
383 | "outputs": [],
384 | "source": []
385 | }
386 | ],
387 | "metadata": {
388 | "kernelspec": {
389 | "display_name": "Python 3",
390 | "language": "python",
391 | "name": "python3"
392 | },
393 | "language_info": {
394 | "codemirror_mode": {
395 | "name": "ipython",
396 | "version": 3
397 | },
398 | "file_extension": ".py",
399 | "mimetype": "text/x-python",
400 | "name": "python",
401 | "nbconvert_exporter": "python",
402 | "pygments_lexer": "ipython3",
403 | "version": "3.6.5"
404 | }
405 | },
406 | "nbformat": 4,
407 | "nbformat_minor": 2
408 | }
409 |
--------------------------------------------------------------------------------
/problems/data_science/chipmunks/logo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/data_science/chipmunks/logo@3x.png
--------------------------------------------------------------------------------
/problems/data_science/chipmunks/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy
2 | pandas
3 | matplotlib
4 | seaborn
5 | sklearn
6 | statsmodels
7 | scipy
8 | jupyter
9 |
--------------------------------------------------------------------------------
/problems/data_science/github_jobs_api/README.md:
--------------------------------------------------------------------------------
1 | # GitHub Jobs API
2 |
3 | The intro for this workshop is on the [Python Project Night webpage](http://chicagopython.github.io/posts/github-jobs-api/). We want to explore the GitHub Jobs API and see what we can learn.
4 |
5 |
6 | * Their wepage is: https://jobs.github.com
7 | * If we navigate to the bottom of the page, we will see their API documentation: https://jobs.github.com/api
8 |
9 |
10 | ## Challenge #0: Getting acquainted
11 |
12 | The first part of using an API us understanding how to interface with it. The GitHub Jobs API is an HTTP endpoint where the user sends an HTTP request (like for a webpage) and gets back a response. For the GitHub API, the response will be a JSON-formatted string. You can even see it in your browser:
13 |
14 | * This URL is for the regular HTML: https://jobs.github.com/positions?description=python&location=chicago
15 | * This URL is for the JSON: https://jobs.github.com/positions.json?description=python&location=chicago
16 |
17 | It is hard for a human to interpret JSON-formatted data; a viewer may help. To explore the format of the data, feel free to go to https://codebeautify.org/jsonviewer
18 |
19 | ### [codebeautify.org](https://codebeautify.org/jsonviewer)
20 |
21 | * Click the **Load URL** button and paste the URL for the JSON in the popup window.
22 | * You can also expand the window so it's not just half of the screen.
23 |
24 | Go back to the API documentation and iterate a bit between the viewer page and the docs until you understand the format of the API:
25 |
26 | * Try example queries with the other fields, such as **description**, **location**, **full_time**
27 | * Try the pagination
28 |
29 |
30 | ## Challenge #1: Pull the data
31 |
32 | OK, Python! Open up a text editor to save your code, and a Python shell to experiment in. You will probably want to use
33 |
34 | * [`requests`](https://2.python-requests.org) for the HTTP GET request -- `pip install requests`
35 | * You will have to pass query parameters using the `params` keyword argument ([documentation](https://2.python-requests.org/en/master/user/quickstart/#passing-parameters-in-urls))
36 | * It has a [built-in JSON decoder](https://2.python-requests.org/en/master/user/quickstart/#json-response-content)
37 |
38 |
39 | Possible suggestions
40 |
41 | * It may be helpful to pull one single job at first to be able to parse the formatting
42 | ```
43 | https://jobs.github.com/positions/.json
44 | ```
45 | * You may want to write a function that wraps the HTTP request so you only need to send it parameters for the query
46 | * Advanced users may be interested in writing a [generator](https://docs.python.org/3/howto/functional.html#generators) for the pagination
47 |
48 |
49 | Don't spend too much time on this step -- the fun part is data exploration!
50 |
51 |
52 | ## Challenge #2: Exploratory Data Analysis (EDA)
53 |
54 | Exploratory Data Analysis is really what it sounds like: bumbling around in the weeds of the dataset _exploring_ what is there. So literally go key by key in the dictionary (JSON) response object and see what's there. You don't have to look at every key and value...but do dig a little into the things that seem promising, surprising, or interesting.
55 |
56 | * There's nothing wrong with simply counting results from queries with different **description** strings!
57 | * Though you can try things like checking for a string `"foo" in "FOO data bar data baz baz baz".lower()`
58 | * Or the adventurous may try regex, e.g. `import re; re.search("foo", "FOO data bar data baz baz baz", re.IGNORECASE)`
59 | * You may like the `Counter` object from [`collections`](https://docs.python.org/3/library/collections.html#counter-objects) in the Python standard library.
60 |
61 | For example, if you had the response data in an object called `results` you could group by company like this:
62 |
63 | ```python
64 | all_the_companies = Counter(result['company'] for result in results)
65 | ```
66 |
67 | Or if you want to search for a specific set of languages (maybe the ones you know!)...you can do that.
68 |
69 |
70 |
71 | ## Challenge #3: Visualization
72 |
73 | Exploring data can be aided significantly by plotting the data. It's often easier to visualize insights than to learn them just from looking at data. Visualizations are also a great way to summarize information when sharing your findings.
74 |
75 | If you're not working in a Jupyter notebook, consider creating one now to make inspecting the visualizations easier. In your environment, `pip install jupyter`. Then type `jupyter notebook` from your terminal.
76 |
77 | One cool visualization you might consider making is an upset plot. This chart type allows you to plot multiple set intersections (like a Venn Diagram on steroids). Luckily, a Python library called UpsetPlot exists to make creation easy. Let's `pip install upsetplot` from our terminal (or `!pip install --quiet upsetplot` from within Jupyter). An example of what your upsetplot might look like:
78 |
79 | 
80 |
81 | ### Some questions you might want to consider:
82 |
83 | Does the dataset tell you anything about which companies use the GitHub for hiring at all? In which cities? Should we move to a different part of the country? Should I learn Go? Kubernetes? Is Data Science saturated? Growing? Does one company's job posts overwhelm the dataset?
84 |
85 | - Where might you get additional information?
86 | - How often should you pull the data? What would capture over time give you?
87 | - Choose your own adventure and share with the group what you have learned!
88 |
--------------------------------------------------------------------------------
/problems/data_science/github_jobs_api/requirements.txt:
--------------------------------------------------------------------------------
1 | requests
2 |
--------------------------------------------------------------------------------
/problems/data_science/github_jobs_api/upsetplot-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/data_science/github_jobs_api/upsetplot-example.png
--------------------------------------------------------------------------------
/problems/data_science/home_credit_default_risk/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | name = "pypi"
3 | url = "https://pypi.org/simple"
4 | verify_ssl = true
5 |
6 | [dev-packages]
7 |
8 | [packages]
9 | jupyterlab = "~=1.1.4"
10 | pandas = "==0.25.1"
11 | seaborn = "==0.9.0"
12 | sklearn = "==0.21.3"
13 |
14 |
15 | [requires]
16 | python_version = "3.7"
--------------------------------------------------------------------------------
/problems/data_science/home_credit_default_risk/README.md:
--------------------------------------------------------------------------------
1 | ## Overview
2 |
3 | Many people struggle to get loans due to insufficient or non-existent credit histories. And, unfortunately, this population is often taken advantage of by untrustworthy lenders.
4 |
5 | Tonight's project examines a dataset from a real bank that focuses on lending to people with little or no credit history. Their goal is to ensure that clients capable of repayment are not rejected. You will explore the dataset and make predictions whether someone will default or not, based on their application for a loan.
6 |
7 | ## Your Task
8 |
9 | Your goal is to train a binary classification model on the data in `default_risk_train_data.csv` that optimized area under the ROC curve between the predicted probability and the observed target. For each `SK_ID_CURR` in `default_risk_train_data.csv`, you must predict a probability for the TARGET variable. Your deliverable to the bank will be a CSV with predictions for each SK_ID_CURR in the test set.
10 |
11 | ## Setup
12 |
13 | 1. For this challenge you will need Python 3.7, pipenv, and git installed. If you're not familiar with pipenv, it's a packaing tool for Python that effectively replaced the pip+virtualenv+requirements.txt workflow. If you already have pip installed, the easiest way to install pipenv is with `pip install --user pipenv`; however, a better way for Mac/Linux Homebrew users is to instead run `brew install pipenv`. More options can be found [here](https://pipenv-fork.readthedocs.io/en/latest/install.html#installing-pipenv).
14 |
15 | 2. The project is in the ChiPy project night repo. If you do not have the repository already, run
16 |
17 | ```
18 | git clone https://github.com/chicagopython/CodingWorkshops.git
19 | ```
20 |
21 | 3. Navigate to the folder for this challenge:
22 |
23 | ```
24 | cd CodingWorkshops/problems/data_science/home_credit_default_risk
25 | ```
26 |
27 | 4. Run `pipenv install`, which will install all of the libraries we have recommended for this exercise.
28 | 5. After you've installed all of the libraries, run `pipenv shell`, which will turn on a virtual environment running Python 3.7.
29 | 6. From within the shell, run `jupyter lab default_risk.ipynb` to launch the pre-started notebook.
30 | 7. To exit the pipenv shell when you are done, simply type `exit`.
31 |
32 | ## What's in this repository?
33 |
34 | There are three data files, one metadata file, and a jupyter notebook.
35 |
36 | 1. default_risk_train_data.csv -- The data you will use to train your models. Includes all potential features and the target.
37 | 2. default_risk_test_data.csv -- The data you will use to test your models. Includes all potential features, but NOT the target (which theoretically reflect unknown future default status).
38 | 3. perfect_deliverable.csv -- The CSV with perfect predictions for each SK_ID_CURR in the test set. You should only use this at the very end to test the model and NEVER factor it into training your model. To prevent overfitting, you should test models sparingly. This is the same format the final deliverable should be submitted to the bank in.
39 | 4. default_risk_column_descriptions.csv -- Descriptive metadata for the columns found in the train and test datasets.
40 | 5. default_risk.ipynb -- The jupyer notebook where all coding should be completed, unless you opt to work in a different environment.
41 |
42 | This project is based on a Kaggle competition, with a subset of the data provided for the sake of download size. Note that this data has not been cleaned for you, and you should expect to deal with real world data issues, such as missing values, bad values, class imbalances, etc.
43 |
44 | ## So what should we do?
45 |
46 | To successfully complete this challenge, you'll need to:
47 | 1. become an expert on the data,
48 | 2. clean the data,
49 | 3. engineer the features for your model(s),
50 | 4. test/validate your models,
51 | 5. generate the deliverable the bank expects.
52 |
53 | Here are some tips/questions to consider along the way:
54 | - Identify which columns are numerical and which are categorical
55 | - Which columns are missing values, and what should be done about the missing values?
56 | - Which features are relevant and why?
57 | - Which features might you want to remove?
58 | - What new features might you create?
59 | - How will you deal with categorical data (e.g. Label Encoding, One-Hot encoding, etc).
60 | - Is there any class imbalance?
61 | - What models will you try? sklearn has been installed in your environment; and [linear regression](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html), [logistic regression](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html#sklearn.linear_model.LogisticRegression), and [random forest](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html#sklearn.ensemble.RandomForestClassifier) models have been imported in the given notebook. Feel free, however, to use the library/models of your choice.
62 |
--------------------------------------------------------------------------------
/problems/data_science/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask
2 | Flask_RESTful
3 | numpy
4 | pandas
5 | python-dateutil
6 | pytz
7 | scikit-learn
8 | scipy
9 | six
10 | sklearn
11 | jupyter
12 | altair
13 | vega
14 |
--------------------------------------------------------------------------------
/problems/data_science/team_project.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "## Data Analysis using Pandas\n",
8 | "Pandas has become the defacto package for data analysis. In this workshop, we are going to use the basics of pandas to analyze the interests of today's group. We are going to use meetup.com's api and fetch the list of interests that are listed in each of our meetup.com profile. We will compute which interests are common, which are uncommon, and find out how we can use topics of common interest to form teams for project night. \n",
9 | "\n",
10 | "Lets get started by importing the essentials. You would need meetup.com's python api and pandas installed."
11 | ]
12 | },
13 | {
14 | "cell_type": "code",
15 | "execution_count": 1,
16 | "metadata": {},
17 | "outputs": [],
18 | "source": [
19 | "!pip install meetup-api\n",
20 | "import meetup.api\n",
21 | "import pandas as pd\n",
22 | "from IPython.display import Image, display, HTML\n",
23 | "from itertools import combinations\n",
24 | "import sys"
25 | ]
26 | },
27 | {
28 | "cell_type": "markdown",
29 | "metadata": {},
30 | "source": [
31 | "Next one of you need get a meetup.com API key. You will find it https://secure.meetup.com/meetup_api/key/ Also you'll need tonight's event id. Tonight's event id is the nine digit number in the meetup url."
32 | ]
33 | },
34 | {
35 | "cell_type": "code",
36 | "execution_count": 73,
37 | "metadata": {},
38 | "outputs": [],
39 | "source": [
40 | "API_KEY = '3f6d3275d3b6314e73453c4aa27'\n",
41 | "event_id='239174132'"
42 | ]
43 | },
44 | {
45 | "cell_type": "markdown",
46 | "metadata": {},
47 | "source": [
48 | "The following function uses the api and loads the data into a pandas data frame."
49 | ]
50 | },
51 | {
52 | "cell_type": "code",
53 | "execution_count": 74,
54 | "metadata": {},
55 | "outputs": [],
56 | "source": [
57 | "def get_members(event_id):\n",
58 | " client = meetup.api.Client(API_KEY)\n",
59 | " rsvps=client.GetRsvps(event_id=event_id, urlname='_ChiPy_')\n",
60 | " member_id = ','.join([str(i['member']['member_id']) for i in rsvps.results])\n",
61 | " return client.GetMembers(member_id=member_id)\n",
62 | "\n",
63 | "def get_topics(members):\n",
64 | " topics = set()\n",
65 | " for member in members.results:\n",
66 | " try:\n",
67 | " for t in member['topics']:\n",
68 | " topics.add(t['name'])\n",
69 | " except:\n",
70 | " print(\"Unexpected error:\", sys.exc_info()[0])\n",
71 | " raise\n",
72 | "\n",
73 | " return list(topics)\n",
74 | "\n",
75 | "def df_topics(event_id):\n",
76 | " members = get_members(event_id=event_id)\n",
77 | " topics = get_topics(members)\n",
78 | " columns=['name','id','thumb_link'] + topics\n",
79 | " \n",
80 | " data = [] \n",
81 | " for member in members.results:\n",
82 | " topic_vector = [0]*len(topics)\n",
83 | " for topic in member['topics']:\n",
84 | " index = topics.index(topic['name']) \n",
85 | " topic_vector[index] = 1\n",
86 | " try:\n",
87 | " data.append([member['name'], member['id'], member['photo']['thumb_link']] + topic_vector)\n",
88 | " except KeyError:\n",
89 | " data.append([member['name'], member['id'], 'NA'] + topic_vector)\n",
90 | " except:\n",
91 | " print(\"Unexpected error:\", sys.exc_info()[0])\n",
92 | " raise\n",
93 | " return pd.DataFrame(data=data, columns=columns)"
94 | ]
95 | },
96 | {
97 | "cell_type": "markdown",
98 | "metadata": {},
99 | "source": [
100 | "### Q1: Load data from meetup.com into a dataframe by calling df_topics.\n",
101 | "You'll need to call the `df_topics` function with the `event_id` and assign it to a variable to use it for the following questions."
102 | ]
103 | },
104 | {
105 | "cell_type": "code",
106 | "execution_count": null,
107 | "metadata": {},
108 | "outputs": [],
109 | "source": []
110 | },
111 | {
112 | "cell_type": "markdown",
113 | "metadata": {},
114 | "source": [
115 | "### Q2: What are the column names of the dataframe?"
116 | ]
117 | },
118 | {
119 | "cell_type": "code",
120 | "execution_count": null,
121 | "metadata": {},
122 | "outputs": [],
123 | "source": []
124 | },
125 | {
126 | "cell_type": "markdown",
127 | "metadata": {},
128 | "source": [
129 | "### Q3: How do you check the index of the dataframe? Can you set the index of the data frame to be the names column? "
130 | ]
131 | },
132 | {
133 | "cell_type": "code",
134 | "execution_count": null,
135 | "metadata": {},
136 | "outputs": [],
137 | "source": []
138 | },
139 | {
140 | "cell_type": "markdown",
141 | "metadata": {},
142 | "source": [
143 | "### Q4: How would you get the transpose of the dataframe?"
144 | ]
145 | },
146 | {
147 | "cell_type": "code",
148 | "execution_count": null,
149 | "metadata": {},
150 | "outputs": [],
151 | "source": []
152 | },
153 | {
154 | "cell_type": "markdown",
155 | "metadata": {},
156 | "source": [
157 | "### Q5: What does the first and last 10 rows of the dataset look like?"
158 | ]
159 | },
160 | {
161 | "cell_type": "code",
162 | "execution_count": null,
163 | "metadata": {
164 | "collapsed": true
165 | },
166 | "outputs": [],
167 | "source": []
168 | },
169 | {
170 | "cell_type": "markdown",
171 | "metadata": {},
172 | "source": [
173 | "### Q6: Write the data out to a csv file. Only include names and topics. Do not include member id and thumblink."
174 | ]
175 | },
176 | {
177 | "cell_type": "code",
178 | "execution_count": null,
179 | "metadata": {
180 | "collapsed": true
181 | },
182 | "outputs": [],
183 | "source": []
184 | },
185 | {
186 | "cell_type": "markdown",
187 | "metadata": {},
188 | "source": [
189 | "### Q7: How many unique topics of interest do we have?"
190 | ]
191 | },
192 | {
193 | "cell_type": "code",
194 | "execution_count": null,
195 | "metadata": {
196 | "collapsed": true
197 | },
198 | "outputs": [],
199 | "source": []
200 | },
201 | {
202 | "cell_type": "markdown",
203 | "metadata": {},
204 | "source": [
205 | "### Q8: Write a function that takes a name and gives back all of his/her interest."
206 | ]
207 | },
208 | {
209 | "cell_type": "code",
210 | "execution_count": null,
211 | "metadata": {
212 | "collapsed": true
213 | },
214 | "outputs": [],
215 | "source": []
216 | },
217 | {
218 | "cell_type": "markdown",
219 | "metadata": {},
220 | "source": [
221 | "### Q9: Write a function that takes a topic of interest and gives back names who are interested."
222 | ]
223 | },
224 | {
225 | "cell_type": "code",
226 | "execution_count": null,
227 | "metadata": {
228 | "collapsed": true
229 | },
230 | "outputs": [],
231 | "source": []
232 | },
233 | {
234 | "cell_type": "markdown",
235 | "metadata": {},
236 | "source": [
237 | "### Q10: Who has the highest number of topics? How many topics is he/she interested in?"
238 | ]
239 | },
240 | {
241 | "cell_type": "code",
242 | "execution_count": null,
243 | "metadata": {
244 | "collapsed": true
245 | },
246 | "outputs": [],
247 | "source": []
248 | },
249 | {
250 | "cell_type": "markdown",
251 | "metadata": {},
252 | "source": [
253 | "### Q11: Which is the most common topic of intertest? Which is the least popular topic of interest?"
254 | ]
255 | },
256 | {
257 | "cell_type": "code",
258 | "execution_count": null,
259 | "metadata": {
260 | "collapsed": true
261 | },
262 | "outputs": [],
263 | "source": []
264 | },
265 | {
266 | "cell_type": "markdown",
267 | "metadata": {},
268 | "source": [
269 | "### Q12: Which names are associated with the topics of interest found in the previous question?"
270 | ]
271 | },
272 | {
273 | "cell_type": "code",
274 | "execution_count": null,
275 | "metadata": {},
276 | "outputs": [],
277 | "source": []
278 | },
279 | {
280 | "cell_type": "markdown",
281 | "metadata": {},
282 | "source": [
283 | "### Q13: Draw a plot that shows the frequency of each topic."
284 | ]
285 | },
286 | {
287 | "cell_type": "code",
288 | "execution_count": null,
289 | "metadata": {
290 | "collapsed": true
291 | },
292 | "outputs": [],
293 | "source": []
294 | },
295 | {
296 | "cell_type": "markdown",
297 | "metadata": {},
298 | "source": [
299 | "### Q14: Are there topic(s) common to all the members of your team?"
300 | ]
301 | },
302 | {
303 | "cell_type": "code",
304 | "execution_count": null,
305 | "metadata": {},
306 | "outputs": [],
307 | "source": []
308 | },
309 | {
310 | "cell_type": "markdown",
311 | "metadata": {},
312 | "source": [
313 | "### Q15: Write a function that will take the names of your team members and rank every pair by the number of topics common among them. So if the team has A, B, C and D, an example could be\n",
314 | "\n",
315 | " A, B - 6\n",
316 | " A, C - 5\n",
317 | " A, D - 4\n",
318 | " B, C - 3\n",
319 | " B, D - 2\n",
320 | " C, D - 1\n",
321 | "\n",
322 | "### Note the pairs are sorted in the number of topics common among them.\n"
323 | ]
324 | },
325 | {
326 | "cell_type": "code",
327 | "execution_count": null,
328 | "metadata": {},
329 | "outputs": [],
330 | "source": []
331 | }
332 | ],
333 | "metadata": {
334 | "kernelspec": {
335 | "display_name": "Python 3",
336 | "language": "python",
337 | "name": "python3"
338 | },
339 | "language_info": {
340 | "codemirror_mode": {
341 | "name": "ipython",
342 | "version": 3
343 | },
344 | "file_extension": ".py",
345 | "mimetype": "text/x-python",
346 | "name": "python",
347 | "nbconvert_exporter": "python",
348 | "pygments_lexer": "ipython3",
349 | "version": "3.4.3"
350 | }
351 | },
352 | "nbformat": 4,
353 | "nbformat_minor": 2
354 | }
355 |
--------------------------------------------------------------------------------
/problems/data_science/text-analysis.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/data_science/text-analysis.jpg
--------------------------------------------------------------------------------
/problems/py101/make_a_game/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | name = "pypi"
3 | url = "https://pypi.org/simple"
4 | verify_ssl = true
5 |
6 | [dev-packages]
7 |
8 | [packages]
9 | pytest = "*"
10 | black = "*"
11 |
12 | [requires]
13 | python_version = "3.7"
14 |
--------------------------------------------------------------------------------
/problems/py101/make_a_game/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "51f48cd2be9338b63b497d9d423d9b639fd84e7e61efb768858d5aef9b173fc8"
5 | },
6 | "pipfile-spec": 6,
7 | "requires": {
8 | "python_version": "3.7"
9 | },
10 | "sources": [
11 | {
12 | "name": "pypi",
13 | "url": "https://pypi.org/simple",
14 | "verify_ssl": true
15 | }
16 | ]
17 | },
18 | "default": {
19 | "appdirs": {
20 | "hashes": [
21 | "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
22 | "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
23 | ],
24 | "version": "==1.4.3"
25 | },
26 | "atomicwrites": {
27 | "hashes": [
28 | "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
29 | "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
30 | ],
31 | "version": "==1.3.0"
32 | },
33 | "attrs": {
34 | "hashes": [
35 | "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
36 | "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
37 | ],
38 | "version": "==19.1.0"
39 | },
40 | "black": {
41 | "hashes": [
42 | "sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf",
43 | "sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"
44 | ],
45 | "index": "pypi",
46 | "version": "==19.3b0"
47 | },
48 | "click": {
49 | "hashes": [
50 | "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
51 | "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
52 | ],
53 | "version": "==7.0"
54 | },
55 | "importlib-metadata": {
56 | "hashes": [
57 | "sha256:9ff1b1c5a354142de080b8a4e9803e5d0d59283c93aed808617c787d16768375",
58 | "sha256:b7143592e374e50584564794fcb8aaf00a23025f9db866627f89a21491847a8d"
59 | ],
60 | "markers": "python_version < '3.8'",
61 | "version": "==0.20"
62 | },
63 | "more-itertools": {
64 | "hashes": [
65 | "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832",
66 | "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"
67 | ],
68 | "version": "==7.2.0"
69 | },
70 | "packaging": {
71 | "hashes": [
72 | "sha256:a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9",
73 | "sha256:c491ca87294da7cc01902edbe30a5bc6c4c28172b5138ab4e4aa1b9d7bfaeafe"
74 | ],
75 | "version": "==19.1"
76 | },
77 | "pluggy": {
78 | "hashes": [
79 | "sha256:0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc",
80 | "sha256:b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c"
81 | ],
82 | "version": "==0.12.0"
83 | },
84 | "py": {
85 | "hashes": [
86 | "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
87 | "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
88 | ],
89 | "version": "==1.8.0"
90 | },
91 | "pyparsing": {
92 | "hashes": [
93 | "sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80",
94 | "sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"
95 | ],
96 | "version": "==2.4.2"
97 | },
98 | "pytest": {
99 | "hashes": [
100 | "sha256:95d13143cc14174ca1a01ec68e84d76ba5d9d493ac02716fd9706c949a505210",
101 | "sha256:b78fe2881323bd44fd9bd76e5317173d4316577e7b1cddebae9136a4495ec865"
102 | ],
103 | "index": "pypi",
104 | "version": "==5.1.2"
105 | },
106 | "six": {
107 | "hashes": [
108 | "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
109 | "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
110 | ],
111 | "version": "==1.12.0"
112 | },
113 | "toml": {
114 | "hashes": [
115 | "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
116 | "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
117 | ],
118 | "version": "==0.10.0"
119 | },
120 | "wcwidth": {
121 | "hashes": [
122 | "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
123 | "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
124 | ],
125 | "version": "==0.1.7"
126 | },
127 | "zipp": {
128 | "hashes": [
129 | "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e",
130 | "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"
131 | ],
132 | "version": "==0.6.0"
133 | }
134 | },
135 | "develop": {}
136 | }
137 |
--------------------------------------------------------------------------------
/problems/py101/make_a_game/README.md:
--------------------------------------------------------------------------------
1 | # Make a Game
2 |
3 | ## Overview
4 |
5 | For a long time, computer games made use of few, if any, graphics. Many of them were text based adventures that you could run directly on your command line. Some examples included:
6 |
7 | - [Zork](https://en.wikipedia.org/wiki/Zork)
8 | - Adventureland
9 | - [Dwarf Fortress](https://en.wikipedia.org/wiki/Dwarf_Fortress)
10 |
11 | and many others. Players would input their directions using words and the computer would return back what happened.
12 |
13 | ## Your Task
14 |
15 | Your task for this evening is to, working together, create something fun to play! Your group will take turns typing (in other words, one computer per group and only one person typing at a time) and helping to develop (offering ideas, thoughts on what to do next, etc.). It can be helpful to have another person with their computer open to research, but ultimately, this is a group effort! Everyone should have a chance to write code, offer suggestions, research libraries, etc.
16 |
17 | ## Setup
18 |
19 | 1. You'll need one computer that your group will share that can install and run [Pipenv](https://pipenv-fork.readthedocs.io/en/latest/). While an OS-X or Linux machine will likely do the best for this step, a Windows machine will be able to do it as well. If you run into any challenges installing Pipenv, please ask for help!
20 | 2. The project is in the ChiPy project night repo. If you do not have the repository already, run
21 |
22 | ```
23 | git clone https://github.com/chicagopython/CodingWorkshops.git
24 | ```
25 |
26 | 3. Navigate to the folder for this challenge:
27 |
28 | ```
29 | cd CodingWorkshops/problems/py101/make_a_game
30 | ```
31 |
32 | 4. Run `pipenv install`, which will install all of the libraries we have recommended for this exercise.
33 | 5. After you've installed all of the libraries, run `pipenv shell`, which will turn on a virtual environment running Python 3.7.
34 | 6. Run `python run.py` to see the program in its current state or `pytest -vv` to run all tests.
35 | 7. If you make changes, this project uses a library called [Black](https://github.com/psf/black) to automatically format the code for you (this known as a [linter](https://en.wikipedia.org/wiki/Lint_(software)). To run it, from the root of the directory, run `black .`
36 |
37 | ## What's in this repository?
38 |
39 | In this repository is a basic shell of a game. This game sets up a `Player()` which parrots back what the player writes to it until they decide to leave. Some of the key features here that you might want to use or modify or extend are:
40 |
41 | 1. _Tests_ -- in the `tests/` folder are a series of tests to make sure that the `Player()` object continues to work as expected. As you add new functionality, you might want to practice [test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) to ensure that your code continues to work as you want it to!
42 | 2. _run.py_ -- This is the main file that the player will run to play the game. One thing to note is the section that starts with `while player.in_game:` -- this section sets up a loop that will keep running until the `in_game` attribute is set to False. This way, your players can continue to do things and the game won't run once through the code and immediately finish. You'll likely add extra things into this section.
43 | 3. _Player() class_ -- This class holds information about the player -- what its name is, what message it wants to repeat, whether it still wants to play the game...classes are useful for persisting or modifying some sort of collected state or values about a "thing", as well as defining actions that that thing may take. For example, our `Player()` can currently `say_hello()` and it has an `in_game` status that can be either `True` or `False`. A different object might have different behaviors or different attributes that can be set. Depending on your game, you may want to set up more of these classes -- for example, you could set up a `Map()` class to hold onto information about a map (what room the player is currently in, what rooms they can go to, etc.) or an `Enemy()` class (what the enemy can do, how it interacts with the player, whether it is defeated or not, etc.
44 |
45 | ## So what should we do?
46 |
47 | A good way to begin might be the following:
48 |
49 | 1. Decide what type of game you want to make: do you want to make a madlibs clone? Tic-tac-toe? A small dungeon? A word game? Put together a couple of ideas and identify what you'd like to build (and don't worry if you don't finish in time! This exercise is for you to be introduced to some Python concepts, not to emerge with a fully-developed game).
50 | 2. Identify what basic building blocks you would need to interact with in the game. For example, if you were making a madlibs clone, you would want to identify what the user could enter, some scripts for those words to be entered into, and something that reads the story out after all the words have been entered. This can help with figuring out the basic flow of the game (for example, you would not want the story to be revealed before all the words are entered!)
51 | 3. Start adding code and testing the game -- you could both add automated tests (like the ones in `tests/` or try playing your game to see if it works.
52 |
53 | Happy Developing!
54 |
--------------------------------------------------------------------------------
/problems/py101/make_a_game/lib/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/py101/make_a_game/lib/__init__.py
--------------------------------------------------------------------------------
/problems/py101/make_a_game/lib/player.py:
--------------------------------------------------------------------------------
1 | class Player:
2 | def __init__(self):
3 | self._in_game = True
4 | self.name = None
5 |
6 | def say_hello(self):
7 | return "Hello!"
8 |
9 | def set_name(self, name):
10 | self.name = name
11 |
12 | def repeat(self, message):
13 | return message
14 |
15 | @property
16 | def in_game(self):
17 | return self._in_game
18 |
19 | @in_game.setter
20 | def in_game(self, game_state):
21 | if not isinstance(game_state, bool):
22 | raise Exception("Must set game state to True or False")
23 | self._in_game = game_state
24 |
--------------------------------------------------------------------------------
/problems/py101/make_a_game/run.py:
--------------------------------------------------------------------------------
1 | from lib.player import Player
2 |
3 | if __name__ == "__main__":
4 | player = Player()
5 | player_name = input("Hello! What is your name? ")
6 | player.set_name(player_name)
7 | print(f"Welcome {player.name} to this new game!")
8 | print("Right now, all we can do is repeat what you say.")
9 | print("Edit this program to add in more functionality!")
10 | print("To stop playing, type quit")
11 |
12 | while player.in_game:
13 | message = input("What would you like me to repeat? (type quit to exit) ")
14 | if message == "quit":
15 | print("Roger that, thanks for playing!")
16 | player.in_game = False
17 | else:
18 | print(player.repeat(message))
19 |
--------------------------------------------------------------------------------
/problems/py101/make_a_game/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/py101/make_a_game/tests/__init__.py
--------------------------------------------------------------------------------
/problems/py101/make_a_game/tests/test_player.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from lib.player import Player
4 |
5 |
6 | class TestPlayer:
7 | def test_player_says_hello(self):
8 | assert Player().say_hello() == "Hello!"
9 |
10 | def test_player_sets_name(self):
11 | player = Player()
12 | player.set_name("Player Name")
13 | assert player.name == "Player Name"
14 |
15 | def test_player_repeats_message(self):
16 | assert Player().repeat("A message") == "A message"
17 |
18 | def test_player_updates_play_state(self):
19 | player = Player()
20 | # assert default player state
21 | assert player.in_game is True
22 | # assert we can set player to inactive
23 | player.in_game = False
24 | # assert that the player state has changed
25 | assert player.in_game is False
26 |
27 | def test_cannot_update_player_to_invalid_state(self):
28 | player = Player()
29 | with pytest.raises(Exception):
30 | player.in_game = "Some kind of invalid state!"
31 |
--------------------------------------------------------------------------------
/problems/py101/python_koans/README.md:
--------------------------------------------------------------------------------
1 | For this exercise we will learn the Zen of Python using Test Driven Development.
2 | Python Koans is a suite of broken tests, which are written against Python code that demonstrate how to Pythonic code.
3 | Your job is to fix the broken tests by filling in the missing parts of the code.
4 |
5 | * Download the zip of python_koans from [here](https://github.com/tathagata/python_koans/archive/chipy_mentorship_coding_dojo.zip). This is a fork of the original repository without some of the simpler examples.
6 |
7 | * Unzip the archive. Change into the directory created and then depending on which version of Python you
8 | would be using, change into python2 or python3 directory.
9 |
10 | * Run `./run.sh` or `./run.bat` depending on if you are in a unix or windows environment.
11 |
12 | * You'll see an output like
13 | > Thinking AboutLists
14 | > test_creating_lists has damaged your karma.
15 | >
16 | > You have not yet reached enlightenment ...
17 | > AssertionError: '-=> FILL ME IN! <=-' != 0
18 |
19 | > Please meditate on the following code:
20 | > File "/Users/t/Downloads/python_koans-chipy_mentorship_coding_dojo_2/python3/koans/about_lists.py", line 14, in test_creating_lists
21 | > self.assertEqual(__, len(empty_list))
22 | >
23 | >
24 | > You have completed 0 koans and 1 lessons.
25 | > You are now 206 koans and 36 lessons away from reaching enlightenment.
26 | >
27 | > Beautiful is better than ugly.
28 |
29 | * Open the file that follows "Please meditate on the following code" in your text editor and put the appropriate fix.
30 |
31 | * Run `./run.sh` or `./run.bat` depending on if you are in a unix or windows environment. If your fix is correct, you'll see the error message has been replaced with a new one. Great! you have fixed one test, so now move on to the next one by repeating the above steps.
32 |
--------------------------------------------------------------------------------
/problems/py101/python_team_project/README.md:
--------------------------------------------------------------------------------
1 | The organizers of Project Nights need your help! Grouping people for
2 | team projects is a manual task. Why do it manually, when
3 | we can automate it?
4 |
5 | ### Is this project for you
6 | Before you progress further, let's check if we are ready to solve this. You should
7 | - Have a personal computer with working wifi and power cord
8 | - Have Python 3 installed on your computer. Yep, Python 3 only.
9 | - Have [Atom](https://atom.io/) or [Sublime Text](https://www.sublimetext.com/3) installed in your computer.
10 | - Have written & ran programs in Python from the command line
11 | - Have some idea about lists, dictionaries and functions
12 | - Have some idea about `virtualenv` and installing packages with `pip`
13 |
14 | This project is not tested using Jupyter Notebook, PyCharm,
15 | Spider, or any other ide/text editor/programming environment for that matter.
16 | Atom or Sublime Text and the command line are the only supported development environment for this project.
17 |
18 | Short url for this page: **https://git.io/vdv43**
19 |
20 | Sounds reasonable? Then let's dive in - and build an awesome command line app using python.
21 |
22 |
23 | ### Can command line applications be cool
24 | You bet!
25 | Checkout this PyCon 2017 video on which this project is based
26 |
27 |
29 |
30 | The slides are available [here](https://speakerdeck.com/pycon2017/amjith-ramanujam-awesome-command-line-tools).
31 |
32 | ### What is a Team Project
33 | Glad you asked! A team project is an hour long problem solving session where each team
34 | consists of four members of different expertise level. The teams are formed from the
35 | list of attendees of the project night.
36 |
37 |
38 | ### The Objective
39 | Our objective is to build an awesome command line application in Python3 that
40 | - allows creating list of people who want to participate in a team project
41 | - once the list is created, the program automatically creates teams of four
42 |
43 | ### A Balanced Team
44 | To keep the team composition balanced in terms of experience, we want every team
45 | to have two members with more experience than the other two.
46 | Measuring experience is very subjective and difficult, but we will keep it simple.
47 | We will rely on a (not very scientific) metic - lines of code written till date.
48 |
49 | We will create a list by taking names of people from tonight's RSVP list. Along with their name we will also include the number of lines of code that person has written till date in Python or an equivalent language. Imagine this as a tool that one of the organizers uses to checkin attendees as they start coming in on the day of Project Night.
50 |
51 | And yeah, this number of lines can be just a rough estimate. As a
52 | reference, the linux kernel is over 23 million lines of code!
53 |
54 | ### Bootstrap
55 | - If you are familiar with `git`, run
56 |
57 | git clone https://github.com/chicagopython/CodingWorkshops.git
58 |
59 | - If not, go to https://github.com/chicagopython/CodingWorkshops
60 | - Click on the Download Zip and unzip the file that gets downloaded
61 | - From your command line, change directory to the path where you have downloaded it.
62 | - On linux or OS X
63 |
64 | > cd path/to/CodingWorkshops/problems/py101/python_team_project/
65 |
66 | - On Windows
67 |
68 | > cd path\to\CodingWorkshops\problems\py101\python_team_project
69 |
70 |
71 | Here you will find the basic skeleton of the app under `app.py`. (after September 21, 2017)
72 |
73 | ### Set up virtualenv
74 | If you are using Linux or OS X, run the following to create a new virtualenv
75 |
76 | python3 -m venv venv
77 | source venv/bin/activate
78 | pip install -r requirements.txt
79 | python app.py
80 |
81 | On Windows, run the following
82 |
83 | python3 -m venv venv
84 | venv\Scripts\activate
85 | pip install -r requirements.txt
86 | python app.py
87 |
88 | [](https://asciinema.org/a/M1hP91h153PuOPEjVYbot6jPj)
89 |
90 | Next let's get started by looking into the code.
91 |
92 | ## Feature 0: Look into app.py
93 | app.py is the script contains some code to get you started.
94 | We will be using two external libraries for this
95 | program.
96 |
97 | python_prompt_toolkit
98 | meetup-api
99 |
100 | - `prompt_toolkit` makes it easy for building awesome command line apps
101 | - `meetup-api` provides us with the data for the meetup
102 | - `asciinema` which is also in the `requirements.txt` isn't strictly necessary and we'll talk about it last
103 |
104 | `execute` function is where you would be writing your application logic.
105 |
106 | You should not require to make changes to `main` and `get_names` functions. In an upcoming project nights we will dig into `get_names` and make changes to it.
107 |
108 | Next let's run app.py
109 |
110 | python3 app.py
111 |
112 | This should drop you to a prompt.
113 |
114 | >
115 |
116 | Type in something to that prompt.
117 |
118 | > Hola amigo
119 | > You issued: Hola amigo
120 |
121 | Try a few more
122 |
123 | > Gracias
124 | > You issued:Gracias
125 |
126 | You can now press the up arrow key and access the history of the commands you have issued. To exit out of the program, you can type Ctrl-D.
127 |
128 | >
129 | GoodBye!
130 |
131 | ### Feature 1: Implement the Add command
132 | Next let's create a command where the user of the program can register new participants to build up the list of users from whom teams will be formed.
133 |
134 | The command should look like the following
135 |
136 | add
137 |
138 | where is the full name of the person as it appears in the
139 | meetup.com and is the number of lines of code
140 | that person has written in Python or a similar programming language in their life.
141 |
142 | > add Tathagata Dasgupta 1
143 |
144 |
145 | ### Feature 2: Add some error checking (optional)
146 | You might be asking what if the user incorrectly types something that is not a number
147 | for the `number of lines`. Indeed that would be incorrect. Show an error message
148 | if is not a number.
149 |
150 | > add Tathagata Dasgupta o
151 | ERROR: number of lines should be, er, a "number"
152 |
153 | Are there other error conditions that can arise?
154 |
155 | ## Feature 2: Implement a List command
156 | Next add a new command list.
157 | Show the number of people added and prints the total count
158 | and the median of the line count.
159 |
160 | > add Tathagata Dasgupta 1
161 | > add Jason Wirth 2
162 | > add Adam Bain 3
163 | > add Brian Ray 4
164 | > add Guido van Rossum 5
165 | > list
166 | > People added so far:
167 | Tathagata Dasgupta, 1
168 | Jason Wirth, 2
169 | Adam Bain, 3
170 | Brian Ray, 4
171 | Guido Van Rossum, 5
172 |
173 | Number of people: 5
174 | Median line count: 3
175 |
176 | Your output need not be exactly the same, but should show the
177 | correct data. The Median line count will be used in the next
178 | features.
179 | Hint: Python3 has the statistics module, so you can use
180 |
181 | import statistics
182 | statistics.median([1,2,3,4,5])
183 |
184 |
185 | ## Feature 3: Add the teams command (optional)
186 | The next command we will implement is `teams` command. Let's say you
187 | have added a few people already and know what the median line count
188 | is for the people you have added so far. On issuing the `teams` command
189 | it should output teams of four such that each team contains
190 | - 2 person who have written less than the median lines of code
191 | - 2 person who has written more than written more than median
192 |
193 | If there are less the four people left to group, then group them
194 | together.
195 |
196 | With our running example, there would be a team of four, and the
197 | remaining 1 should be in another group.
198 |
199 | > teams
200 | Group 1: Tathagata Dasgupta, Jason Wirth, Brian Ray, Guido Van Rossum
201 | Group 2: Adam Bain
202 |
203 |
204 | ## Feature 4. Enhance Team command (optional)
205 | Add a unique team name
206 |
207 | ## Feature 5. Enhance Team command (optional)
208 | Make up random room names and add a room name for each team.
209 |
210 | ## Feature 6. Enhance Teams command (optional)
211 | Print the teams sorted with the average number of lines of code for each team.
212 |
213 | ## Feature 7. Auto-completion for commands (optional)
214 | Adding auto completion is easy with `prompt_toolkit`. In `app.py` the following line is used to include the
215 | `add` command to auto-completion.
216 |
217 | command_completer = WordCompleter(['add'], ignore_case=True)
218 |
219 | Add the remaining commands.
220 |
221 |
222 | ## Feature 8. Auto-completion for participant names (optional)
223 | Typing in names of the attendees of project night would be time consuming
224 | and error prone. Let's add auto-completion magic to it!
225 |
226 | The funcion `get_names` uses meetup-api and returns a list of names for the attendees.
227 | All you need to do is include a call to `get_names` in the command_completer.
228 |
229 | ## Feature 9. Tell the world (optional, OS X or Linux only)
230 | We have also installed asciinema - a tool that allows you
231 | to create recordings of your terminal sessions. In order to tell
232 | the world what your team has made, let's make a small recording.
233 |
234 | ascriinmea rec teamname.json
235 |
236 | Run your program and show off all the cool features you have built in your app.
237 | To finish recording hit Ctrl-D.
238 | Next play the recordings
239 |
240 | asciinema play teamname.json
241 |
242 | Once the playback looks good, upload it to the interwebs.
243 |
244 | asciinema upload teamname.json
245 |
246 | Finally, tweet the link to @chicagopython with "Python Project Night
247 | Mentorship". Include the twitter handles of your team members.
248 |
249 | Note: This is tested only in OS X. Let me know your experience for running it on
250 | other operating systems.
251 | If you see an error
252 |
253 | asciinema needs a UTF-8 native locale to run. Check the output of `locale` command.
254 |
255 | the run the following command before running asciinema.
256 |
257 | export LC_ALL=en_US.UTF-8
258 |
259 |
260 | Thanks! Thats all folks!
261 | If you found a bug or think you some instructions are missing - just open a issue in this repository.
262 |
--------------------------------------------------------------------------------
/problems/py101/python_team_project/app.py:
--------------------------------------------------------------------------------
1 | from __future__ import unicode_literals
2 | import sys
3 |
4 | from prompt_toolkit import prompt, AbortAction
5 | from prompt_toolkit.history import InMemoryHistory
6 | from prompt_toolkit.contrib.completers import WordCompleter
7 | import meetup.api
8 |
9 | def get_names():
10 | client = meetup.api.Client('3f6d3275d3b6314e73453c4aa27')
11 |
12 | rsvps=client.GetRsvps(event_id='239174106', urlname='_ChiPy_')
13 | member_id = ','.join([str(i['member']['member_id']) for i in rsvps.results])
14 | members = client.GetMembers(member_id=member_id)
15 |
16 | names = []
17 | for member in members.results:
18 | try:
19 | names.append(member['name'])
20 | except:
21 | pass # ignore those who do not have a complete profile
22 |
23 | return names
24 |
25 |
26 | command_completer = WordCompleter(['add'], ignore_case=True)
27 |
28 |
29 | def execute(command):
30 | return "You issued:" + command
31 |
32 |
33 | def main():
34 | history = InMemoryHistory()
35 |
36 | while True:
37 | try:
38 | text = prompt('> ',
39 | completer = command_completer,
40 | history=history,
41 | on_abort=AbortAction.RETRY)
42 | messages = execute(text)
43 |
44 | print(messages)
45 | except EOFError:
46 | break # Control-D pressed.
47 |
48 | print('GoodBye!')
49 |
50 | if __name__ == '__main__':
51 | main()
52 |
--------------------------------------------------------------------------------
/problems/py101/python_team_project/requirements.txt:
--------------------------------------------------------------------------------
1 | prompt_toolkit==1.0.15
2 | meetup-api==0.1.1
3 | asciinema==1.4.0
4 |
--------------------------------------------------------------------------------
/problems/py101/testing/EnableTravisCI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/py101/testing/EnableTravisCI.png
--------------------------------------------------------------------------------
/problems/py101/testing/Makefile:
--------------------------------------------------------------------------------
1 | all:setup test
2 |
3 | .PHONY: all help setup test
4 |
5 | help:
6 | @echo "Usage:"
7 | @echo " make help show this message"
8 | @echo " make setup create virtual environment and install dependencies"
9 | @echo " make test run the test suite"
10 | @echo " exit leave virtual environment"
11 |
12 | setup:
13 | pip install pipenv
14 | pipenv install --dev --three
15 |
16 | test:
17 | pipenv run -- pytest
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/problems/py101/testing/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.org/simple"
3 | verify_ssl = true
4 | name = "pypi"
5 |
6 | [packages]
7 | pytest = "==3.6.3"
8 | pytest-cov = "==2.5.1"
9 | pytest-xdist = "==1.19.1"
10 | "pytest-flake8" = "==1.0.1"
11 |
12 | [dev-packages]
13 |
14 | [requires]
15 | python_version = "3.6"
16 |
--------------------------------------------------------------------------------
/problems/py101/testing/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "cf193a18372660cf547b480da0f6d2960693fa8075bb401cf4f90bbc5e6de9e5"
5 | },
6 | "pipfile-spec": 6,
7 | "requires": {
8 | "python_version": "3.6"
9 | },
10 | "sources": [
11 | {
12 | "name": "pypi",
13 | "url": "https://pypi.org/simple",
14 | "verify_ssl": true
15 | }
16 | ]
17 | },
18 | "default": {
19 | "apipkg": {
20 | "hashes": [
21 | "sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6",
22 | "sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c"
23 | ],
24 | "markers": "python_version >= '2.7' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.0.*' and python_version != '3.3.*'",
25 | "version": "==1.5"
26 | },
27 | "atomicwrites": {
28 | "hashes": [
29 | "sha256:240831ea22da9ab882b551b31d4225591e5e447a68c5e188db5b89ca1d487585",
30 | "sha256:a24da68318b08ac9c9c45029f4a10371ab5b20e4226738e150e6e7c571630ae6"
31 | ],
32 | "version": "==1.1.5"
33 | },
34 | "attrs": {
35 | "hashes": [
36 | "sha256:4b90b09eeeb9b88c35bc642cbac057e45a5fd85367b985bd2809c62b7b939265",
37 | "sha256:e0d0eb91441a3b53dab4d9b743eafc1ac44476296a2053b6ca3af0b139faf87b"
38 | ],
39 | "version": "==18.1.0"
40 | },
41 | "coverage": {
42 | "hashes": [
43 | "sha256:03481e81d558d30d230bc12999e3edffe392d244349a90f4ef9b88425fac74ba",
44 | "sha256:0b136648de27201056c1869a6c0d4e23f464750fd9a9ba9750b8336a244429ed",
45 | "sha256:198626739a79b09fa0a2f06e083ffd12eb55449b5f8bfdbeed1df4910b2ca640",
46 | "sha256:28b2191e7283f4f3568962e373b47ef7f0392993bb6660d079c62bd50fe9d162",
47 | "sha256:2eb564bbf7816a9d68dd3369a510be3327f1c618d2357fa6b1216994c2e3d508",
48 | "sha256:337ded681dd2ef9ca04ef5d93cfc87e52e09db2594c296b4a0a3662cb1b41249",
49 | "sha256:3a2184c6d797a125dca8367878d3b9a178b6fdd05fdc2d35d758c3006a1cd694",
50 | "sha256:3c79a6f7b95751cdebcd9037e4d06f8d5a9b60e4ed0cd231342aa8ad7124882a",
51 | "sha256:3d72c20bd105022d29b14a7d628462ebdc61de2f303322c0212a054352f3b287",
52 | "sha256:3eb42bf89a6be7deb64116dd1cc4b08171734d721e7a7e57ad64cc4ef29ed2f1",
53 | "sha256:4635a184d0bbe537aa185a34193898eee409332a8ccb27eea36f262566585000",
54 | "sha256:56e448f051a201c5ebbaa86a5efd0ca90d327204d8b059ab25ad0f35fbfd79f1",
55 | "sha256:5a13ea7911ff5e1796b6d5e4fbbf6952381a611209b736d48e675c2756f3f74e",
56 | "sha256:69bf008a06b76619d3c3f3b1983f5145c75a305a0fea513aca094cae5c40a8f5",
57 | "sha256:6bc583dc18d5979dc0f6cec26a8603129de0304d5ae1f17e57a12834e7235062",
58 | "sha256:701cd6093d63e6b8ad7009d8a92425428bc4d6e7ab8d75efbb665c806c1d79ba",
59 | "sha256:7608a3dd5d73cb06c531b8925e0ef8d3de31fed2544a7de6c63960a1e73ea4bc",
60 | "sha256:76ecd006d1d8f739430ec50cc872889af1f9c1b6b8f48e29941814b09b0fd3cc",
61 | "sha256:7aa36d2b844a3e4a4b356708d79fd2c260281a7390d678a10b91ca595ddc9e99",
62 | "sha256:7d3f553904b0c5c016d1dad058a7554c7ac4c91a789fca496e7d8347ad040653",
63 | "sha256:7e1fe19bd6dce69d9fd159d8e4a80a8f52101380d5d3a4d374b6d3eae0e5de9c",
64 | "sha256:8c3cb8c35ec4d9506979b4cf90ee9918bc2e49f84189d9bf5c36c0c1119c6558",
65 | "sha256:9d6dd10d49e01571bf6e147d3b505141ffc093a06756c60b053a859cb2128b1f",
66 | "sha256:be6cfcd8053d13f5f5eeb284aa8a814220c3da1b0078fa859011c7fffd86dab9",
67 | "sha256:c1bb572fab8208c400adaf06a8133ac0712179a334c09224fb11393e920abcdd",
68 | "sha256:de4418dadaa1c01d497e539210cb6baa015965526ff5afc078c57ca69160108d",
69 | "sha256:e05cb4d9aad6233d67e0541caa7e511fa4047ed7750ec2510d466e806e0255d6",
70 | "sha256:f3f501f345f24383c0000395b26b726e46758b71393267aeae0bd36f8b3ade80"
71 | ],
72 | "markers": "python_version >= '2.6' and python_version != '3.1.*' and python_version != '3.2.*' and python_version < '4' and python_version != '3.0.*'",
73 | "version": "==4.5.1"
74 | },
75 | "execnet": {
76 | "hashes": [
77 | "sha256:a7a84d5fa07a089186a329528f127c9d73b9de57f1a1131b82bb5320ee651f6a",
78 | "sha256:fc155a6b553c66c838d1a22dba1dc9f5f505c43285a878c6f74a79c024750b83"
79 | ],
80 | "markers": "python_version >= '2.7' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.0.*' and python_version != '3.3.*'",
81 | "version": "==1.5.0"
82 | },
83 | "flake8": {
84 | "hashes": [
85 | "sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0",
86 | "sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37"
87 | ],
88 | "version": "==3.5.0"
89 | },
90 | "mccabe": {
91 | "hashes": [
92 | "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
93 | "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
94 | ],
95 | "version": "==0.6.1"
96 | },
97 | "more-itertools": {
98 | "hashes": [
99 | "sha256:2b6b9893337bfd9166bee6a62c2b0c9fe7735dcf85948b387ec8cba30e85d8e8",
100 | "sha256:6703844a52d3588f951883005efcf555e49566a48afd4db4e965d69b883980d3",
101 | "sha256:a18d870ef2ffca2b8463c0070ad17b5978056f403fb64e3f15fe62a52db21cc0"
102 | ],
103 | "version": "==4.2.0"
104 | },
105 | "pluggy": {
106 | "hashes": [
107 | "sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff",
108 | "sha256:d345c8fe681115900d6da8d048ba67c25df42973bda370783cd58826442dcd7c",
109 | "sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5"
110 | ],
111 | "markers": "python_version >= '2.7' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.0.*' and python_version != '3.3.*'",
112 | "version": "==0.6.0"
113 | },
114 | "py": {
115 | "hashes": [
116 | "sha256:3fd59af7435864e1a243790d322d763925431213b6b8529c6ca71081ace3bbf7",
117 | "sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e"
118 | ],
119 | "markers": "python_version >= '2.7' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.0.*' and python_version != '3.3.*'",
120 | "version": "==1.5.4"
121 | },
122 | "pycodestyle": {
123 | "hashes": [
124 | "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766",
125 | "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9"
126 | ],
127 | "version": "==2.3.1"
128 | },
129 | "pyflakes": {
130 | "hashes": [
131 | "sha256:08bd6a50edf8cffa9fa09a463063c425ecaaf10d1eb0335a7e8b1401aef89e6f",
132 | "sha256:8d616a382f243dbf19b54743f280b80198be0bca3a5396f1d2e1fca6223e8805"
133 | ],
134 | "version": "==1.6.0"
135 | },
136 | "pytest": {
137 | "hashes": [
138 | "sha256:0453c8676c2bee6feb0434748b068d5510273a916295fd61d306c4f22fbfd752",
139 | "sha256:4b208614ae6d98195430ad6bde03641c78553acee7c83cec2e85d613c0cd383d"
140 | ],
141 | "index": "pypi",
142 | "version": "==3.6.3"
143 | },
144 | "pytest-cov": {
145 | "hashes": [
146 | "sha256:03aa752cf11db41d281ea1d807d954c4eda35cfa1b21d6971966cc041bbf6e2d",
147 | "sha256:890fe5565400902b0c78b5357004aab1c814115894f4f21370e2433256a3eeec"
148 | ],
149 | "index": "pypi",
150 | "version": "==2.5.1"
151 | },
152 | "pytest-flake8": {
153 | "hashes": [
154 | "sha256:e5cdc4f459c9436ac6c649e428a014bb5988605858549397374ec29a776cae68",
155 | "sha256:ec248d4a215d6c7cd9d3ca48f365ece0e3892b46d626c22a95ccc80188ff35ed"
156 | ],
157 | "index": "pypi",
158 | "version": "==1.0.1"
159 | },
160 | "pytest-forked": {
161 | "hashes": [
162 | "sha256:e4500cd0509ec4a26535f7d4112a8cc0f17d3a41c29ffd4eab479d2a55b30805",
163 | "sha256:f275cb48a73fc61a6710726348e1da6d68a978f0ec0c54ece5a5fae5977e5a08"
164 | ],
165 | "version": "==0.2"
166 | },
167 | "pytest-xdist": {
168 | "hashes": [
169 | "sha256:237a0c30056d539de723a1461cf2817c04a9abc90ee1fe51ddfaecc643ef3022"
170 | ],
171 | "index": "pypi",
172 | "version": "==1.19.1"
173 | },
174 | "six": {
175 | "hashes": [
176 | "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
177 | "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
178 | ],
179 | "version": "==1.11.0"
180 | }
181 | },
182 | "develop": {}
183 | }
184 |
--------------------------------------------------------------------------------
/problems/py101/testing/README.md:
--------------------------------------------------------------------------------
1 | # 1. Introduction to PyTest and Continuous Integration
2 |
3 |
4 |
5 | - [1. Introduction to PyTest and Continuous Integration](#1-introduction-to-pytest-and-continuous-integration)
6 | - [1.1. Setup Instructions](#11-setup-instructions)
7 | - [1.1.1. Git and Github](#111-git-and-github)
8 | - [1.1.2. Travis setup](#112-travis-setup)
9 | - [1.2. Python](#12-python)
10 | - [1.3. Quick Git command refresher](#13-quick-git-command-refresher)
11 | - [1.4. Exercise 0: Project Setup](#14-exercise-0-project-setup)
12 | - [1.4.1. `team_organizer.py`](#141-team_organizerpy)
13 | - [1.4.2. `test_team_organizer.py`](#142-test_team_organizerpy)
14 | - [1.4.3. `Makefile`](#143-makefile)
15 | - [1.4.4. `Pipfile` and `Pipfile.lock`](#144-pipfile-and-pipfilelock)
16 | - [1.4.5. `pytest.ini`](#145-pytestini)
17 | - [1.4.6. `travis.yml`](#146-travisyml)
18 | - [1.4.7. Test your setup is working](#147-test-your-setup-is-working)
19 | - [1.5. Exercise 1: Build](#15-exercise-1-build)
20 | - [1.6. Exercise 2: Run the program](#16-exercise-2-run-the-program)
21 | - [1.7. Exercise 3: Running the tests](#17-exercise-3-running-the-tests)
22 | - [1.8. Execrise 4: Coverage](#18-execrise-4-coverage)
23 | - [1.9. Exercise 6: Fail, Fix, Pass](#19-exercise-6-fail-fix-pass)
24 | - [1.10. Exercise 7: Fixtures](#110-exercise-7-fixtures)
25 | - [1.11. Exercise 8: Implement the tests](#111-exercise-8-implement-the-tests)
26 | - [1.12. Exercise 9: Implement the tests first, then implement the feature](#112-exercise-9-implement-the-tests-first-then-implement-the-feature)
27 |
28 |
29 |
30 | Testing and Continuous Integration is at the heart of building good software.
31 | For this project we will be focus on writing tests for a given problem and use
32 | travis-ci for running the tests automatically everytime code is checked into Github.
33 |
34 | **Objectives**:
35 | In this project we will explore
36 |
37 | - Introduction to unit testing with pytest
38 | - How to setup continuous integration with Github and Travis-CI
39 |
40 | ## 1.1. Setup Instructions
41 |
42 | For doing this project you will need a Github account, a Travis-ci.org account and git
43 | installed locally.
44 |
45 | ### 1.1.1. Git and Github
46 |
47 | After completing the steps below you should have a github account and be able to push
48 | your local changes to this repository to github.
49 |
50 | - Follow the setup steps described [here](https://help.github.com/articles/set-up-git/)
51 | - Read the steps described in [fork a repo](https://help.github.com/articles/fork-a-repo)
52 | - Use the steps described above to fork this repository [CodingWorkshops](https://github.com/chicagopython/CodingWorkshops)
53 |
54 | The changes that you make as a part of this exercise, will be pushed to the fork you created for this
55 | repository.
56 |
57 | In case you have already have created a fork of this repository in your github account, you will
58 | want to bring it up to date with the recent changes. In that case,
59 | you will need to do the following:
60 |
61 | - [configuring a remote fork](https://help.github.com/articles/configuring-a-remote-for-a-fork/)
62 | - [syncing a fok](https://help.github.com/articles/syncing-a-fork/)
63 |
64 | ### 1.1.2. Travis setup
65 |
66 | Continuous Integrration is a critical part of building your software. It automatically runs
67 | the tests when you check in code into your version control (git) and paves the way for
68 | continuous delivery, i.e. release often and release early.
69 | In this section we will set up a Continuous Integration pipeline with Travis-ci.
70 |
71 | - First, head over to [Travis-CI.org](https://travis-ci.org/.)
72 | - Sign in with your Github account, and accept the terms and conditions.
73 | - On success, you will be landing on your profile page that lists the CodingWorkshop repository
74 | - Once you have located the repo, toggle the button next to the repository to enable travis CI
75 |
76 | 
77 |
78 | If you have multiple repositories, you will have to search for the repository by typing in the name
79 | of the repository (CodingWorkshop) in the search bar on the dashboard page.
80 |
81 | ## 1.2. Python
82 |
83 | This project has made no attempt to be compatible with Python 2.7. 😎
84 |
85 | Recommended version: Python 3.6
86 |
87 | ## 1.3. Quick Git command refresher
88 |
89 | Below are the few most used git commands
90 |
91 | git checkout master # checkout to master branch
92 | git checkout -b feature/cool # crate a new branch feature/cool
93 | git add -u # stage all the updates for commit
94 | git commit -am "Adding changes and commiting with a comment"
95 | git push origin master # push commits to develop/ci branch
96 |
97 | Note for this exercise, we will be working on the master branch directly. However,
98 | that is NOT the best practice. Branches are cheap in git, so a new feature or fix
99 | would first go to a branch, get tested, code reviewed and finally merged to master.
100 |
101 | ## 1.4. Exercise 0: Project Setup
102 |
103 | After completing the steps in setup, you should have the cloned versoin of the fork of CodingWorkshop
104 | repository in your local machine. Lets take the time to look at the structure of this
105 | project. All code is located under `/problems/py101/testing` directory. So from your
106 | terminal go to the directory where you have cloned the repository.
107 |
108 | cd path/to/clone/problems/py101/testing
109 |
110 | Make sure you are in this directory for the remainder of this project.
111 |
112 | Run `pwd` (`cwd` for Windows) on the command prompt to find out which directory you
113 | are on.
114 |
115 | Your output should end in `problems/py101/testing` and contain the files described
116 | below.
117 |
118 | ### 1.4.1. `team_organizer.py`
119 |
120 | This file is a simplified implementation of the problem of grouping the project
121 | night attendees into teams of four based on the number of lines of code they have
122 | written such that in each team, two team members have more lines of code than the other.
123 | This is the system under test.
124 |
125 | ### 1.4.2. `test_team_organizer.py`
126 |
127 | This file is the test for the above module written using Pytest.
128 |
129 | These two files mentioned above are the only two files that we will be making
130 | modifications to for this project.
131 |
132 | ### 1.4.3. `Makefile`
133 |
134 | This file contains the commands that are required building the project.
135 | You can run `make help` to see what are the options.
136 |
137 | ### 1.4.4. `Pipfile` and `Pipfile.lock`
138 |
139 | These two files are used by `pipenv` to create a virtual enviornment that
140 | isolates all the dependencies of this project from other python projects in your computer.
141 | Learn more about [pipenv](https://docs.pipenv.org/).
142 |
143 | ### 1.4.5. `pytest.ini`
144 |
145 | This file contains the configuration for `pytest`.
146 |
147 | ### 1.4.6. `travis.yml`
148 |
149 | In addition to all the files in this directory, located at the root of the repository,
150 | is a file called `.travis.yml`. This is used by the continuous intergration tool travis-ci.
151 | This contains the information on how to build this python project.
152 |
153 | ### 1.4.7. Test your setup is working
154 |
155 | Just make a small edit on this file (README.md), commit and push the changes.
156 |
157 | git commit -am "Demo commit to check everything is working"
158 | git push origin master
159 |
160 | If travis-ci.org gets triggered and is all green, your push has successfully ran through
161 | the linting and testing pipeline.
162 |
163 | To display that badge of honor, click on the build button next on the travis page and select
164 | Markdown from the second dropdown. Copy the markdown code displayed and add it to the top
165 | of this file (README.md).
166 |
167 | 
168 |
169 | If you run into issues, [ask your question on slack](https://chipy.slack.com/messages/C093F7W8P/details/)
170 |
171 | ## 1.5. Exercise 1: Build
172 |
173 | From the `/problems/py101/testing` directory, run
174 |
175 | make
176 |
177 | - Which packages got installed?
178 | - Which version of python is getting used?
179 | - How many tests pass, skipped and how long did it take?
180 | - Note a new directory `htmlcov` was created. We will revisit this in Exericse 5.
181 | - What is difference in output when you run the `make` command again?
182 |
183 | ## 1.6. Exercise 2: Run the program
184 |
185 | Start by running
186 |
187 | python team_organizer.py
188 |
189 | This will drop you to the program's interactive prompt.
190 | Below is a sample interaction where users named a, b, c,
191 | d, e and f are added using the add command.
192 | Following that, we run the `print` command where the users
193 | are grouped in to max of size four where two users have
194 | written less lines of code than the others.
195 |
196 | ```
197 | t (master *) testing $ python team_organizer.py
198 | Welcome to Chicago Python Project Night Team Organizer
199 | org> help
200 | help
201 |
202 | Documented commands (type help ):
203 | ========================================
204 | add help print
205 |
206 | Undocumented commands:
207 | ======================
208 | exit
209 |
210 | org> help add
211 | help add
212 | Adds a new user. Needs Name slackhandle number_of_lines separated by space
213 | org> add a @a 100
214 | add a @a 100
215 | org> add b @b 200
216 | add b @b 200
217 | org> add c @c 300
218 | add c @c 300
219 | org> add d @d 400
220 | add d @d 400
221 | org> add e @e 500
222 | add e @e 500
223 | org> add f @f 50
224 | add f @f 50
225 | org> print
226 | print
227 | ['f, a, e, d']
228 | b, c
229 | org>
230 | ```
231 |
232 | ## 1.7. Exercise 3: Running the tests
233 |
234 | Run
235 |
236 | make test
237 |
238 | This will run the tests in the `test_team_organizer.py` file.
239 |
240 | Run
241 |
242 | pipenv run pytest --help
243 |
244 | Now check the flags that are present in the `pytest.ini` file against
245 | the output of the `--help` command to see what each one does.
246 |
247 | ## 1.8. Execrise 4: Coverage
248 |
249 | When we first ran `make`, `pytest` created a directory called `htmlcov`
250 | that show you the coverage information about `team_organizr,py` code.
251 | Open the `index.html` file inside `htmlcov` to check the lines that
252 | has not been covered by the tests in the `test_team_organizer.py`.
253 |
254 | What is the % coverage of the code at this point?
255 | Click on `team_organizer.py` to see which lines are outside coverage.
256 |
257 | ## 1.9. Exercise 6: Fail, Fix, Pass
258 |
259 | You are now all set to fix the tests. Goto `test_team_organizer.py` and
260 | find `test_add_a_person_with_lower_than_median` test. Notice this test is
261 | skipped when run with pytest. To fix it remove the decorator `pytest.mark.skip`
262 | and run `pytest` again. Commit the code and run
263 |
264 | make test
265 |
266 | Make the necessary changes so that the test passes.
267 |
268 | git commit -am "Fixed failing test"
269 | git push origin master
270 |
271 | Go to travis-ci.org and inspect the output before and after fixing the test.
272 | What is the coverage value at this point?
273 |
274 | ## 1.10. Exercise 7: Fixtures
275 |
276 | The purpose of test fixtures is to provide a fixed baseline upon which tests can
277 | reliably and repeatedly execute.
278 |
279 | We are making use of two fixtures - one factory method `person` that churns out Persons
280 | as needed by `organizer` fixture.
281 |
282 | `test_count_number_of_teams` is broken as well. How can you fix it?
283 |
284 | Tip: To run a singe test, use
285 |
286 | pipenv run pytest -k
287 |
288 | ## 1.11. Exercise 8: Implement the tests
289 |
290 | The two functions below have been left for you to implement.
291 |
292 | - test_add_a_person_who_has_never_written_code_before
293 | - test_add_two_person_with_same_name_but_different_slack_handles
294 |
295 | Note the names of the tests are long and verbose to give you an idea of what
296 | what exactly you need to test.
297 |
298 | Does implementing these tests have any effect on coverage results?
299 | Would it be still useful if there is no improvement in coverage?
300 |
301 | ## 1.12. Exercise 9: Implement the tests first, then implement the feature
302 |
303 | For the following two tests, first implement the test that asserts the
304 | expected behavior. From the test name it should be evident from the test name.
305 | If you run the tests at this point, they should fail. Then go back to
306 | `team_organizer.py` and implement the feature by changing the code.
307 | Once your implementation is complete, run `make test`.
308 |
309 | - test_adding_person_with_negative_lines_of_code_throws_exception
310 | - test_handle_duplicate_additions
311 |
--------------------------------------------------------------------------------
/problems/py101/testing/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | addopts = --cov=team_organizer --cov-report=term-missing --flake8 --cov-report=html
3 |
--------------------------------------------------------------------------------
/problems/py101/testing/team_organizer.py:
--------------------------------------------------------------------------------
1 | import cmd
2 | import collections
3 |
4 | Person = collections.namedtuple('Person', 'name slackhandle lines')
5 |
6 |
7 | def parse(arg):
8 | try:
9 | name, slackhandle, lines = arg.split()
10 | return Person(name, slackhandle, int(lines))
11 | except ValueError:
12 | print("Invalid entry format")
13 |
14 |
15 | class Organizer:
16 | def __init__(self):
17 | self.d = collections.deque()
18 |
19 | def median_lines(self):
20 | return self.d[len(self.d) // 2].lines
21 |
22 | def add(self, p):
23 | '''Takes a person and adds it to the queue
24 | if the number of lines is higher than the median
25 | it adds to the end else it adds to the front '''
26 |
27 | if len(self.d) == 0:
28 | self.d.append(p)
29 | return
30 |
31 | if self.median_lines() >= p.lines:
32 | self.d.appendleft(p)
33 | else:
34 | self.d.append(p)
35 |
36 | @staticmethod
37 | def _names(persons):
38 | return ', '.join(p.name for p in persons)
39 |
40 | def teams(self):
41 | ''' Makes teams by taking 2 from the front of the queue
42 | and two from the end of the queue till teams of 4
43 | can be made. '''
44 | teams = []
45 | while len(self.d) >= 4:
46 | ps = self.d.popleft(), self.d.popleft(), self.d.pop(), self.d.pop()
47 | teams.append([self._names(ps)])
48 |
49 | if self.d:
50 | teams.append(self._names(self.d))
51 | self.d.clear()
52 | return teams
53 |
54 |
55 | class OrganizerShell(cmd.Cmd):
56 | intro = 'Welcome to Chicago Python Project Night Team Organizer'
57 | prompt = 'org> '
58 |
59 | def __init__(self):
60 | cmd.Cmd.__init__(self)
61 | self.org = Organizer()
62 |
63 | def do_add(self, arg):
64 | 'Adds a new user. Needs Name, slackhandle, number of lines'
65 | p = parse(arg)
66 | self.org.add(p)
67 |
68 | def do_print(self, arg):
69 | 'Print the team organization'
70 | for team in self.org.teams():
71 | print(team)
72 |
73 | def do_exit(self, args):
74 | return True
75 |
76 |
77 | if __name__ == '__main__':
78 | OrganizerShell().cmdloop()
79 |
--------------------------------------------------------------------------------
/problems/py101/testing/test_team_organizer.py:
--------------------------------------------------------------------------------
1 | from team_organizer import Person, Organizer
2 | import pytest
3 | import random
4 | import string
5 |
6 |
7 | @pytest.fixture
8 | def person():
9 | ''' Factory function that returns a function to get new Person '''
10 | def rand_data_person():
11 | name = ''.join(random.sample(string.ascii_lowercase, 3))
12 | handle = '@' + name
13 | lines = random.randint(0, 100000)
14 | return Person(name, handle, lines)
15 | return rand_data_person
16 |
17 |
18 | @pytest.fixture
19 | def organizer(person):
20 | '''Uses the person fixture factory to create an organizer
21 | and adds 4 persons to it.'''
22 | org = Organizer()
23 | for _ in range(4):
24 | org.add(person())
25 |
26 | return org
27 |
28 |
29 | def test_add_person_with_higher_than_median(organizer):
30 | median_lines = organizer.median_lines()
31 | more_than_median_lines = median_lines + 1000
32 | p = Person('a', '@a', more_than_median_lines)
33 | organizer.add(p)
34 |
35 | assert organizer.d[len(organizer.d) - 1] == p
36 |
37 |
38 | @pytest.mark.skip(reason="broken test needs fixing")
39 | def test_add_a_person_with_lower_than_median(organizer):
40 | median_lines = organizer.median_lines()
41 | less_than_median_lines = median_lines - 1000
42 | p = Person('a', '@a', less_than_median_lines)
43 | organizer.add(p)
44 |
45 | assert 1/0 == 0
46 | assert organizer.d[0] == p
47 | pass
48 |
49 |
50 | # Pytest Fixture: https://docs.pytest.org/en/latest/fixture.html
51 | @pytest.mark.skip(reason="broken test needs fixing")
52 | @pytest.mark.parametrize("test_input,expected", [
53 | ([Person('a', '@a', 1),
54 | Person('b', '@b', 2),
55 | Person('c', '@c', 3),
56 | Person('d', '@d', 4)], 1),
57 | ([Person('a', '@a', 1)], 1)
58 | ])
59 | def test_count_number_of_teams(organizer, test_input, expected):
60 | for p in test_input:
61 | organizer.add(p)
62 | assert len(organizer.teams()) == expected
63 |
64 |
65 | def test_add_a_person_who_has_never_written_code_before(organizer):
66 | organizer.add(Person('a', '@a', 0))
67 | pass
68 |
69 |
70 | def test_add_two_person_with_same_name_but_different_slack_handles(organizer):
71 | pass
72 |
73 |
74 | def test_add_a_person_who_supplied_negative_lines_of_code(organizer):
75 | 'Behavior not implemented. Decide & implement the behavior & the test.'
76 | pass
77 |
78 |
79 | def test_add_same_person_twice():
80 | 'Behavior not implemented. Decide & implement the behavior & the test.'
81 | pass
82 |
--------------------------------------------------------------------------------
/problems/py101/testing/travis-build-img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/py101/testing/travis-build-img.png
--------------------------------------------------------------------------------
/problems/py101/trackcoder/Makefile:
--------------------------------------------------------------------------------
1 | all:setup test
2 |
3 | .PHONY: all help setup shell
4 |
5 | help:
6 | @echo "Usage:"
7 | @echo " make help show this message"
8 | @echo " make setup create virtual environment and install dependencies"
9 | @echo " make test run the test suite"
10 | @echo " exit leave virtual environment"
11 |
12 | setup:
13 | pip install pipenv
14 | pipenv install --dev --three
15 |
16 | shell:
17 | pipenv shell
18 |
19 |
20 |
--------------------------------------------------------------------------------
/problems/py101/trackcoder/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.org/simple"
3 | verify_ssl = true
4 | name = "pypi"
5 |
6 | [packages]
7 | pytest = "*"
8 | prompt_toolkit = "*"
9 | peewee = "*"
10 | click = "*"
11 |
12 | [requires]
13 | python_version = "3.6"
--------------------------------------------------------------------------------
/problems/py101/trackcoder/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "3ad590855f2e73831065bbf03fbd5a6aeb2d130f3a784d2e344ec219d3deae4d"
5 | },
6 | "pipfile-spec": 6,
7 | "requires": {
8 | "python_version": "3.6"
9 | },
10 | "sources": [
11 | {
12 | "name": "pypi",
13 | "url": "https://pypi.org/simple",
14 | "verify_ssl": true
15 | }
16 | ]
17 | },
18 | "default": {
19 | "atomicwrites": {
20 | "hashes": [
21 | "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0",
22 | "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"
23 | ],
24 | "version": "==1.2.1"
25 | },
26 | "attrs": {
27 | "hashes": [
28 | "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
29 | "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"
30 | ],
31 | "version": "==18.2.0"
32 | },
33 | "click": {
34 | "hashes": [
35 | "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d",
36 | "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b"
37 | ],
38 | "index": "pypi",
39 | "version": "==6.7"
40 | },
41 | "more-itertools": {
42 | "hashes": [
43 | "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092",
44 | "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e",
45 | "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"
46 | ],
47 | "version": "==4.3.0"
48 | },
49 | "pathlib2": {
50 | "hashes": [
51 | "sha256:8eb170f8d0d61825e09a95b38be068299ddeda82f35e96c3301a8a5e7604cb83",
52 | "sha256:d1aa2a11ba7b8f7b21ab852b1fb5afb277e1bb99d5dfc663380b5015c0d80c5a"
53 | ],
54 | "markers": "python_version < '3.6'",
55 | "version": "==2.3.2"
56 | },
57 | "peewee": {
58 | "hashes": [
59 | "sha256:bb9ee5e2ca601c82f5e3c8327f19af4b5f9941ae74ec520674165692d3d6ae7a"
60 | ],
61 | "index": "pypi",
62 | "version": "==3.7.0"
63 | },
64 | "pluggy": {
65 | "hashes": [
66 | "sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1",
67 | "sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1"
68 | ],
69 | "version": "==0.7.1"
70 | },
71 | "prompt-toolkit": {
72 | "hashes": [
73 | "sha256:12e076b21178064b5627f74c4819559c125e31046b55a28d5e024b85fef5617e",
74 | "sha256:f2289fe9dd7f27c305421bffe880a543b04cc67660796a2a912595dbcd0d209f",
75 | "sha256:ff58ce8bb82c11c43416dd3eec7701dcbe8c576e2d7649f1d2b9d21a2fd93808"
76 | ],
77 | "index": "pypi",
78 | "version": "==2.0.4"
79 | },
80 | "py": {
81 | "hashes": [
82 | "sha256:06a30435d058473046be836d3fc4f27167fd84c45b99704f2fb5509ef61f9af1",
83 | "sha256:50402e9d1c9005d759426988a492e0edaadb7f4e68bcddfea586bc7432d009c6"
84 | ],
85 | "version": "==1.6.0"
86 | },
87 | "pytest": {
88 | "hashes": [
89 | "sha256:453cbbbe5ce6db38717d282b758b917de84802af4288910c12442984bde7b823",
90 | "sha256:a8a07f84e680482eb51e244370aaf2caa6301ef265f37c2bdefb3dd3b663f99d"
91 | ],
92 | "index": "pypi",
93 | "version": "==3.8.0"
94 | },
95 | "scandir": {
96 | "hashes": [
97 | "sha256:04b8adb105f2ed313a7c2ef0f1cf7aff4871aa7a1883fa4d8c44b5551ab052d6",
98 | "sha256:1444134990356c81d12f30e4b311379acfbbcd03e0bab591de2696a3b126d58e",
99 | "sha256:1b5c314e39f596875e5a95dd81af03730b338c277c54a454226978d5ba95dbb6",
100 | "sha256:346619f72eb0ddc4cf355ceffd225fa52506c92a2ff05318cfabd02a144e7c4e",
101 | "sha256:44975e209c4827fc18a3486f257154d34ec6eaec0f90fef0cca1caa482db7064",
102 | "sha256:61859fd7e40b8c71e609c202db5b0c1dbec0d5c7f1449dec2245575bdc866792",
103 | "sha256:a5e232a0bf188362fa00123cc0bb842d363a292de7126126df5527b6a369586a",
104 | "sha256:c14701409f311e7a9b7ec8e337f0815baf7ac95776cc78b419a1e6d49889a383",
105 | "sha256:c7708f29d843fc2764310732e41f0ce27feadde453261859ec0fca7865dfc41b",
106 | "sha256:c9009c527929f6e25604aec39b0a43c3f831d2947d89d6caaab22f057b7055c8",
107 | "sha256:f5c71e29b4e2af7ccdc03a020c626ede51da471173b4a6ad1e904f2b2e04b4bd"
108 | ],
109 | "markers": "python_version < '3.5'",
110 | "version": "==1.9.0"
111 | },
112 | "six": {
113 | "hashes": [
114 | "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
115 | "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
116 | ],
117 | "version": "==1.11.0"
118 | },
119 | "wcwidth": {
120 | "hashes": [
121 | "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
122 | "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
123 | ],
124 | "version": "==0.1.7"
125 | }
126 | },
127 | "develop": {}
128 | }
129 |
--------------------------------------------------------------------------------
/problems/py101/trackcoder/app.py:
--------------------------------------------------------------------------------
1 | from __future__ import unicode_literals
2 | import sys
3 |
4 | from prompt_toolkit import PromptSession
5 | from prompt_toolkit.history import InMemoryHistory
6 | from prompt_toolkit.completion import WordCompleter
7 | from peewee import *
8 | import datetime
9 | import click
10 |
11 | command_completer = WordCompleter(['add', 'show'], ignore_case=True)
12 |
13 | db = SqliteDatabase('to_do_list.db')
14 |
15 |
16 | class ToDo(Model):
17 | task = CharField(max_length=255)
18 | description = CharField(max_length=255)
19 | timestamp = DateTimeField(default=datetime.datetime.now)
20 | mins = IntegerField()
21 | done = BooleanField(default=True)
22 |
23 | class Meta:
24 | database = db
25 |
26 |
27 | def initialize():
28 | """Connect to database, create tables if they don't exist"""
29 | db.connect()
30 | db.create_tables([ToDo], safe=True)
31 |
32 | def parse(input):
33 | """
34 | a b 10 first blog post
35 | a c 10 finished cli
36 | a p 120
37 | """
38 | input = input.strip()
39 | cmd, task, mins, description = ['']*4
40 | try:
41 | cmd, task, mins, *description = input.split()
42 | mins += 0
43 | description = ' '.join(description)
44 | return cmd, task, mins, description
45 | except TypeError:
46 | pass
47 | # return input, task, mins, description
48 | except ValueError:
49 | return input, task, mins, description
50 |
51 |
52 | def add(**kwargs):
53 | ToDo.create(task=kwargs['task'],
54 | mins=kwargs['mins'],
55 | description=kwargs['description'])
56 |
57 |
58 | def show(**kwargs):
59 | for t in ToDo.select():
60 | print(t.task, t.description, t.mins, t.done)
61 |
62 |
63 | def execute(**kwargs):
64 | cmds = {
65 | 'add': add,
66 | 'show': show
67 | }
68 |
69 | cmds.get(kwargs['cmd'])(**kwargs)
70 |
71 |
72 | @click.command()
73 | @click.option('--interactive', '-i', help='needs some help text', is_flag=True, default=False)
74 | @click.option('--show', '-s', help='needs some help text', is_flag=True, default=False)
75 | @click.option('--add', '-a', nargs=3, type=(click.STRING, int, click.STRING), default=(None, None, None))
76 | def main(interactive, add, show):
77 | initialize()
78 |
79 | if interactive:
80 | history = InMemoryHistory()
81 | session = PromptSession()
82 |
83 | while True:
84 | try:
85 | text = session.prompt('% ')
86 | except KeyboardInterrupt:
87 | continue
88 | except EOFError:
89 | break
90 | else:
91 | try:
92 | cmd, task, mins, description = parse(text)
93 | execute(cmd=cmd, task=task, mins=mins, description=description)
94 | except TypeError:
95 | print("Please check your input")
96 | elif show:
97 | execute(cmd='show')
98 | else:
99 | task, mins, description = add
100 | execute(cmd='add', task=task, mins=mins, description=description)
101 | print('GoodBye!')
102 |
103 |
104 | if __name__ == '__main__':
105 | main()
106 |
--------------------------------------------------------------------------------
/problems/py101/trackcoder/csv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/py101/trackcoder/csv.png
--------------------------------------------------------------------------------
/problems/py101/trackcoder/project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/py101/trackcoder/project.png
--------------------------------------------------------------------------------
/problems/py101/trackcoder/to_do_list.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/py101/trackcoder/to_do_list.db
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/.dockerignore:
--------------------------------------------------------------------------------
1 | .dockerignore
2 | Dockerfile
3 | db.sqlite3
4 | __pycache__
5 | *.pyc
6 | *.pyo
7 | *.pyd
8 | .Python
9 | env
10 | pip-log.txt
11 | pip-delete-this-directory.txt
12 | .tox
13 | .coverage
14 | .coverage.*
15 | .cache
16 | coverage.xml
17 | *,cover
18 | *.log
19 | .git
20 | .ropeproject
21 | node_modules
22 |
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 | *.pkl
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | .hypothesis/
49 | .pytest_cache/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | .static_storage/
58 | .media/
59 | local_settings.py
60 |
61 | # Flask stuff:
62 | instance/
63 | .webassets-cache
64 |
65 | # Scrapy stuff:
66 | .scrapy
67 |
68 | # Sphinx documentation
69 | docs/_build/
70 |
71 | # PyBuilder
72 | target/
73 |
74 | # Jupyter Notebook
75 | .ipynb_checkpoints
76 |
77 | # pyenv
78 | .python-version
79 |
80 | # celery beat schedule file
81 | celerybeat-schedule
82 |
83 | # SageMath parsed files
84 | *.sage.py
85 |
86 | # Environments
87 | .env
88 | .venv
89 | env/
90 | venv/
91 | ENV/
92 | env.bak/
93 | venv.bak/
94 | .envrc
95 |
96 | # Spyder project settings
97 | .spyderproject
98 | .spyproject
99 |
100 | # Rope project settings
101 | .ropeproject
102 |
103 | # mkdocs documentation
104 | /site
105 |
106 | # mypy
107 | .mypy_cache/
108 | .ropeproject
109 |
110 | # editors / IDEs
111 | .vscode/
112 |
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/README.md:
--------------------------------------------------------------------------------
1 | In this project we will be building a CRUD-style web app using Django and SQLite3.
2 |
3 | ## The Project
4 | With growing Project Night attendance, ChiPy would like to better keep track of Challenge participation, and the CSV sign up sheets just aren't cutting it. In this project we'll upgrade things a notch by creating a web app (using Django) to keep track of our data (in a basic SQLite3 database). The goal of our app is to have an easy way to enter and view our data, all while maintaining attendee's privacy. To save some time, the Django project has already been created, as well as the framework for the structure for the app we'll be working on. To learn how to set up a Django app from scratch, check out https://docs.djangoproject.com/en/2.1/intro/tutorial01/#creating-a-project.
5 |
6 | By the end of this challenge, you'll have created a web app that looks something like this:
7 |
8 | 
9 |
10 | ## Setup
11 | 1. Clone the project:
12 |
13 | > git clone https://github.com/chicagopython/CodingWorkshops.git
14 |
15 | 2. Set up a virtual environment, as desired:
16 | ```
17 | # If you are using Linux or OS X, run the following:
18 | > python3 -m venv venv
19 | > source venv/bin/activate
20 |
21 | # On Windows, run the following:
22 | > python3 -m venv venv
23 | > venv\Scripts\activate
24 | ```
25 | 3. Navigate to the right folder:
26 |
27 | > cd problems/webdev/django_pn_tracker
28 |
29 | 4. Install our python package requirements:
30 |
31 | > pip install -r requirements.txt
32 |
33 |
34 | ## Instructions
35 | In the steps that follow, instructions will generally reference exactly where code changes need to be made. In the files themselves you'll see large commented blocks of instructions indicating that you're in the right spot. If you don't see commented instructions, double check that you read the prompt correctly. Each step will also have a link to a resource that should directly help you solve the problem at hand. Even if you're not stuck, it's recommended to check out the link to improve your understanding of WHY we're doing what we're doing.
36 |
37 | To help visualize the locations of the file, here's the full file tree:
38 | ```
39 | ├── db.sqlite3
40 | ├── django_pn_tracker
41 | │ ├── __init__.py
42 | │ ├── __pycache__
43 | │ ├── apps
44 | │ │ ├── __init__.py
45 | │ │ ├── __pycache__
46 | │ │ │ └── __init__.cpython-36.pyc
47 | │ │ └── challenges
48 | │ │ ├── __init__.py
49 | │ │ ├── __pycache__
50 | │ │ ├── admin.py
51 | │ │ ├── apps.py
52 | │ │ ├── forms.py
53 | │ │ ├── migrations
54 | │ │ │ ├── 0001_initial.py
55 | │ │ │ ├── __init__.py
56 | │ │ │ └── __pycache__
57 | │ │ ├── models.py
58 | │ │ ├── templates
59 | │ │ │ └── challenges
60 | │ │ │ ├── delete.html
61 | │ │ │ ├── edit.html
62 | │ │ │ └── list.html
63 | │ │ ├── tests.py
64 | │ │ ├── urls.py
65 | │ │ └── views.py
66 | │ ├── settings.py
67 | │ ├── static
68 | │ │ ├── css
69 | │ │ │ ├── bootstrap.min.css
70 | │ │ │ └── master.css
71 | │ │ └── js
72 | │ │ ├── bootstrap.min.js
73 | │ │ └── main.js
74 | │ ├── templates
75 | │ │ ├── base.html
76 | │ │ └── index.html
77 | │ ├── urls.py
78 | │ ├── views.py
79 | │ └── wsgi.py
80 | ├── manage.py
81 | ├── requirements.txt
82 | └── setup.cfg
83 | ```
84 |
85 | ### Step 0: Run the app as is
86 | Before we dig in, let's see what the app currently looks like. This'll also confirm that install/setup went as planned. To run the app locally:
87 |
88 | > ./manage.py runserver
89 |
90 | Then open the link provided in the terminal: http://127.0.0.1:8000/
91 |
92 | ### Step 1: Configure settings
93 | While our project already has a lot written, we need to configure settings for our new app. Django projects store these settings in `settings.py` by default.
94 |
95 | **a. Add our 'challenges' app to `INSTALLED_APPS`** in `settings.py`. This is actually done already, so the app would run as is without error. Still, check the comment in the code to see how we add apps.
96 |
97 | **b. Point Django to our sqlite db** called `db.sqlite3`. See https://docs.djangoproject.com/en/2.1/ref/settings/#databases for help with the syntax.
98 |
99 | ### Step 2: Create and integrate a new database table
100 | Our initial objective is to set up a table to display all of our challenge participantion records. Table schemas can be found in `models.py`.
101 |
102 | a. Several tables already exist, but we want to create a new table called `AttendeeInfo` to include:
103 |
104 | * `date` - Date of the event
105 | * `name` - the participant's name
106 | * `challenge` - the name of the challenge. Don't forget to account for the foreign key relationship with `Challenge`
107 | * `skills` - for now an integer representing a score in the range of 0-10. Read more avoud validators: https://docs.djangoproject.com/en/2.1/ref/validators/
108 |
109 | Read more about models here: https://docs.djangoproject.com/en/2.1/topics/db/models/
110 |
111 | **b. Create/complete migration.** In order for our changes to take effect we need to create a migration and then actually migrate it.
112 |
113 | > ./manage.py makemigrations
114 |
115 | This is a little bit of Django magic. Under the hood Django is automatically generating the SQL commands necessary to update your database. You can see the actual underlying commands in `apps/challenges/migrations`, where a file of commands is created each time we run makemigrations.
116 |
117 | Normally you would see a message like:
118 |
119 | Migrations for 'challenges':
120 | django_pn_tracker/apps/challenges/migrations/0002_auto_20180816_1445.py
121 | -
122 | ...
123 |
124 | However, since the table for our new model actually already exists in db.sqlite3 (purely for the sake of having example records for later steps), if everything is working correctly so far you should see:
125 |
126 | No changes detected
127 |
128 | In order to actually make our changes, run:
129 |
130 | > ./manage.py migrate
131 |
132 | c. **Register `models.AttendeeInfo` in `admin.py`.** Don't worry about what this does yet, we'll get to it in a later instruction.
133 |
134 | ### Step 3: Create a page to view the table's records
135 | Now that we've created our table, we want to create a page to view our new table's records.
136 |
137 | **a. Create the URL** we want for our page in `urls.py`. We will use `""` and reference `challenges_list` in views. Learn more about Django URLs: https://docs.djangoproject.com/en/2.1/topics/http/urls/
138 |
139 | **b. Create the new `challenge_list` view in `views.py`** for our new page. Django has the concept of “views” to encapsulate the logic responsible for processing a user’s request and for returning the response. Syntactically, a view is just a regular python function (or class) that will be called when we travel to the associated url. To learn more about writing views, check out: https://docs.djangoproject.com/en/2.1/topics/http/views/ . In the case of challenge_list, use the following variable names:
140 |
141 | * `template_name` as the variable that points to `challenges/list.html`,
142 | * `attendees` should be all of our AttendeeInfo objects (see https://docs.djangoproject.com/en/2.1/topics/db/queries/#retrieving-objects), and
143 | * `context` should be a dictionary mapping the string `"attendees"` to our `attendees` variable.
144 |
145 | The names selected are only important to match the templates that we've already started for you.
146 |
147 | **c. Create a template.** Templates are the layers of your app that create the structure of the pages visible to users. Django uses a templating language that's very similar to HTML plus some interactivity with our python code, mostly via syntax surrounded by curly braces. Instead of starting totally from scratch, the template for our record listing is already started in `list.html`, so we'll just fill in the missing section (as indicated with comments). To learn more about template basics (and see some syntax examples) check out: https://docs.djangoproject.com/en/2.1/ref/templates/language/#templates .
148 |
149 | **d. Add a link to our new view in our main navigation bar.** This can be done in the body of `base.html`.
150 |
151 | ### Step 4: Add CRUD capability
152 | Now we can view our new table, but there's nothing in it! Let's create a way to add/edit/delete entries via forms on the front end. To do this, we'll take our steps from 3 and add a little more complexity ala forms:
153 |
154 | **a. Create the URL** we want for our page in `urls.py`. We will reference `challenges_add`, `challenges_edit`, and `challenges_delete` views. Note that edit and delete will reference existing objects - the url pattern therefore requires special syntax (revisit https://docs.djangoproject.com/en/2.1/topics/http/urls/ for help)
155 |
156 | **b. Create forms.** Set up `AttendeeEditForm` and `ConfirmForm` in `forms.py`. We will reference these in our views. Learn more about model forms here: https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/ , and more about fields here: https://docs.djangoproject.com/en/2.1/ref/forms/fields/ .
157 |
158 | **c. Create the three new views in views.py for our new pages.** Mimic the template_name, attendees, and context variable names and style from challenge_list. Use the variable form to instantiate the form object. For example, paste this into challenge_add: `form = forms.AttendeeEditForm()` . See https://docs.djangoproject.com/en/2.1/topics/forms/#the-view and for help.
159 |
160 | **d. Create templates.** These are already started for you in `edit.html` and `delete.html`, so just fill in the missing section (as indicated). Note that add and edit will both use `edit.html`. Bonus hint: Are the edit and delete forms really different..? See https://docs.djangoproject.com/en/2.1/topics/forms/#the-template for help.
161 |
162 | **e. Add links.** Add links for edit and delete in a new column in the existing table (requires editing `list.html` again).
163 |
164 | ### Step 4: Add yourselves as records using our new forms
165 | Now that we've created our MVP, let's test it out by adding records for ourselves for this event. You'll notice that there's no event option in the dropdown for this Intro to Django event. For now, add yourselves under 'Demo Event'.
166 |
167 | ### Step 5: Add interface to add new events
168 | You've seen how to create a form and have a couple of example templates you've already worked with. Now it's time to do one from scratch. Create a form to add challenge records to the Challenge table. You will need a new template in the same folder as our delete.html, edit.html, and list.html. You'll also need to create a new form in forms.py. Lastly we'll need a way to get to our page to add a challenge - let's put it on the main navigation bar next to Challenges List (again in the body of base.html)
169 |
170 | ### Step 6: Add login requirements so our app isn't open to the world.
171 | After all, we'll have participants names and experience levels stored - that's sensitive information. You're on your own again! If you need help:
172 | * https://docs.djangoproject.com/en/2.1/topics/auth/default/#the-login-required-decorator
173 | * https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
174 |
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/db.sqlite3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/webdev/django_pn_tracker/db.sqlite3
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/webdev/django_pn_tracker/django_pn_tracker/__init__.py
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/apps/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/webdev/django_pn_tracker/django_pn_tracker/apps/__init__.py
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/apps/challenges/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/webdev/django_pn_tracker/django_pn_tracker/apps/challenges/__init__.py
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/apps/challenges/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from . import models
3 |
4 | # Learn about the Django Admin:
5 | # https://docs.djangoproject.com/en/2.1/ref/contrib/admin/
6 | class ChallengeAdmin(admin.ModelAdmin):
7 | list_display = ['name', 'description']
8 | search_fields = ['name', 'description']
9 |
10 |
11 | admin.site.register(models.ChallengeTools)
12 | admin.site.register(models.Challenge, ChallengeAdmin)
13 |
14 | ##############################
15 | # Register AttendeeInfo here #
16 | ##############################
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/apps/challenges/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ChallengesConfig(AppConfig):
5 | name = 'challenges'
6 |
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/apps/challenges/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from . import models
3 |
4 |
5 | ########################################
6 | # Fill in all ###s in the forms below. #
7 | # Uncomment the forms when complete. #
8 | ########################################
9 |
10 | # class AttendeeEditForm(forms.ModelForm):
11 |
12 | # class Meta:
13 | # fields = ###
14 | # model = ###
15 |
16 |
17 | # class ConfirmForm(forms.Form):
18 | # pass
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/apps/challenges/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-08-11 21:23
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | initial = True
10 |
11 | dependencies = [
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='AttendeeInfo',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('created_date', models.DateTimeField(auto_now_add=True)),
20 | ('modified_date', models.DateTimeField(auto_now=True)),
21 | ('date', models.DateTimeField()),
22 | ('name', models.CharField(max_length=128, verbose_name='Participant Name')),
23 | ('skills', models.IntegerField(default=0)),
24 | ],
25 | options={
26 | 'abstract': False,
27 | },
28 | ),
29 | migrations.CreateModel(
30 | name='Challenge',
31 | fields=[
32 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
33 | ('created_date', models.DateTimeField(auto_now_add=True)),
34 | ('modified_date', models.DateTimeField(auto_now=True)),
35 | ('name', models.CharField(max_length=128)),
36 | ('description', models.TextField(blank=True, null=True)),
37 | ],
38 | options={
39 | 'abstract': False,
40 | },
41 | ),
42 | migrations.CreateModel(
43 | name='ChallengeTools',
44 | fields=[
45 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
46 | ('created_date', models.DateTimeField(auto_now_add=True)),
47 | ('modified_date', models.DateTimeField(auto_now=True)),
48 | ('name', models.CharField(max_length=128)),
49 | ('description', models.TextField(blank=True, null=True)),
50 | ],
51 | options={
52 | 'verbose_name': 'Challenge Tool',
53 | 'verbose_name_plural': 'Challenge Tools',
54 | },
55 | ),
56 | migrations.AddField(
57 | model_name='challenge',
58 | name='tools',
59 | field=models.ManyToManyField(blank=True, to='challenges.ChallengeTools'),
60 | ),
61 | migrations.AddField(
62 | model_name='attendeeinfo',
63 | name='challenge',
64 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='challenges.Challenge'),
65 | ),
66 | ]
67 |
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/apps/challenges/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/webdev/django_pn_tracker/django_pn_tracker/apps/challenges/migrations/__init__.py
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/apps/challenges/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.core.validators import MaxValueValidator, MinValueValidator
3 |
4 |
5 | class DateModelBase(models.Model):
6 | # Learn about model fields:
7 | # https://docs.djangoproject.com/en/2.1/ref/models/fields/
8 | created_date = models.DateTimeField(auto_now_add=True)
9 | modified_date = models.DateTimeField(auto_now=True)
10 |
11 | class Meta:
12 | # Learn about abstract base classes:
13 | # https://docs.djangoproject.com/en/2.1/topics/db/models/#abstract-base-classes
14 | abstract = True
15 |
16 |
17 | class ChallengeTools(DateModelBase, models.Model):
18 | name = models.CharField(max_length=128)
19 | description = models.TextField(blank=True, null=True)
20 |
21 | def __str__(self):
22 | return f"{self.name}"
23 |
24 | class Meta:
25 | # learn about django model Meta options:
26 | # https://docs.djangoproject.com/en/2.1/ref/models/options/
27 | verbose_name = "Challenge Tool"
28 | verbose_name_plural = "Challenge Tools"
29 |
30 |
31 | class Challenge(DateModelBase, models.Model):
32 | name = models.CharField(max_length=128)
33 | description = models.TextField(blank=True, null=True)
34 | tools = models.ManyToManyField("challenges.ChallengeTools", blank=True)
35 |
36 | def __str__(self):
37 | return f"{self.name}"
38 |
39 |
40 | ########################################
41 | # Fill in all ###s in the model below. #
42 | # Uncomment the class when complete. #
43 | ########################################
44 |
45 | # class AttendeeInfo(###, ###):
46 | # date = ###
47 | # name = ###
48 | # challenge = ###(###, on_delete=models.CASCADE)
49 | # skills = ###
50 | #
51 | # def __str__(self):
52 | # return f"{self.challenge} {self.name}"
53 |
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/apps/challenges/templates/challenges/delete.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 |
Welcome to the Project Night Tracker! Our index page is all set up, but want to start tracking challenge participants. That's where you come in!
11 |
12 |
13 |
14 | {% endblock content %}
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/urls.py:
--------------------------------------------------------------------------------
1 | """regcounter URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/2.1/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.contrib import admin
17 | from django.urls import path, include
18 | from django_pn_tracker import views
19 |
20 |
21 | urlpatterns = [
22 | path('admin/', admin.site.urls),
23 | path('', views.index, name='index'),
24 | path('challenges/', include('django_pn_tracker.apps.challenges.urls')),
25 | ]
26 |
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | def index(request):
4 | template_name = "index.html"
5 | context = {}
6 | return render(request, template_name, context=context)
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/django_pn_tracker/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for regcounter project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'regcounter.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == '__main__':
6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_pn_tracker.settings')
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError as exc:
10 | raise ImportError(
11 | "Couldn't import Django. Are you sure it's installed and "
12 | "available on your PYTHONPATH environment variable? Did you "
13 | "forget to activate a virtual environment?"
14 | ) from exc
15 | execute_from_command_line(sys.argv)
16 |
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/requirements.txt:
--------------------------------------------------------------------------------
1 | django==2.1
2 |
--------------------------------------------------------------------------------
/problems/webdev/django_pn_tracker/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | ignore = D203
3 | exclude = .git,__pycache__,docs/source/conf.py,old,build,dist,*/migrations/*
4 | max-complexity = 10
5 | max-line-length=99
6 |
--------------------------------------------------------------------------------
/problems/webdev/django_rest_framework_api/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | name = "pypi"
3 | url = "https://pypi.org/simple"
4 | verify_ssl = true
5 |
6 | [dev-packages]
7 |
8 | [packages]
9 | Django = "~=2.2.6"
10 | djangorestframework = "~=3.10.3"
11 |
12 |
13 | [requires]
14 | python_version = "3.7"
--------------------------------------------------------------------------------
/problems/webdev/django_rest_framework_api/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "170332a1bcaeaed6de494dfff1768c99e7787199909f96206652f2ce0f2f2bc8"
5 | },
6 | "pipfile-spec": 6,
7 | "requires": {
8 | "python_version": "3.7"
9 | },
10 | "sources": [
11 | {
12 | "name": "pypi",
13 | "url": "https://pypi.org/simple",
14 | "verify_ssl": true
15 | }
16 | ]
17 | },
18 | "default": {
19 | "django": {
20 | "hashes": [
21 | "sha256:16040e1288c6c9f68c6da2fe75ebde83c0a158f6f5d54f4c5177b0c1478c5b86",
22 | "sha256:89c2007ca4fa5b351a51a279eccff298520783b713bf28efb89dfb81c80ea49b"
23 | ],
24 | "index": "pypi",
25 | "version": "==2.2.7"
26 | },
27 | "djangorestframework": {
28 | "hashes": [
29 | "sha256:5488aed8f8df5ec1d70f04b2114abc52ae6729748a176c453313834a9ee179c8",
30 | "sha256:dc81cbf9775c6898a580f6f1f387c4777d12bd87abf0f5406018d32ccae71090"
31 | ],
32 | "index": "pypi",
33 | "version": "==3.10.3"
34 | },
35 | "pytz": {
36 | "hashes": [
37 | "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
38 | "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
39 | ],
40 | "version": "==2019.3"
41 | },
42 | "sqlparse": {
43 | "hashes": [
44 | "sha256:40afe6b8d4b1117e7dff5504d7a8ce07d9a1b15aeeade8a2d10f130a834f8177",
45 | "sha256:7c3dca29c022744e95b547e867cee89f4fce4373f3549ccd8797d8eb52cdb873"
46 | ],
47 | "version": "==0.3.0"
48 | }
49 | },
50 | "develop": {}
51 | }
52 |
--------------------------------------------------------------------------------
/problems/webdev/django_rest_framework_api/README.md:
--------------------------------------------------------------------------------
1 | # Build an API with Django REST Framework
2 |
3 | ## Overview
4 | For this project, we will be creating a functioning REST API. REST APIs can help distribute useful information via GET requests, as well as post and alter databases in a user friendly fashion.
5 |
6 | This project will revolve around using Django and Django's REST framework to build an API for the dataset of your choice. Django is a full web framework capable of handling both back and front end portions of a web app; and the Django team has created great resources to make setting up a Django app quick and easy.
7 |
8 | While the project is structured around Django, feel free to use flask instead, if you're more comfortable.
9 |
10 | ## Environment Setup
11 | To avoid bloating of your primary working environment, we strongly recommend creating a virtual environment. The requirements.txt file includes the required packages, and the included versions have been tested for our needs - use different versions at your own risk.
12 |
13 | We also strong recommend using [Atom](https://atom.io/) or [Sublime Text](https://www.sublimetext.com/3) as your text editor. This project has also NOT been tested using Jupyter Notebook, PyCharm,
14 | Spider, or any other ide/text editor/programming environment.
15 |
16 | 1. For this challenge you will need Python 3.7, pipenv, and git installed. If you're not familiar with pipenv, it's a packaing tool for Python that effectively replaced the pip+virtualenv+requirements.txt workflow. If you already have pip installed, the easiest way to install pipenv is with `pip install --user pipenv`; however, a better way for Mac/Linux Homebrew users is to instead run `brew install pipenv`. More options can be found [here](https://pipenv-fork.readthedocs.io/en/latest/install.html#installing-pipenv).
17 |
18 | 2. The project is in the ChiPy project night repo. If you do not have the repository already, run
19 |
20 | ```
21 | git clone https://github.com/chicagopython/CodingWorkshops.git
22 | ```
23 |
24 | 3. Navigate to the folder for this challenge:
25 |
26 | ```
27 | cd CodingWorkshops/problems/webdev/django_rest_framework_api
28 | ```
29 |
30 | 4. Run `pipenv install`, which will install all of the libraries we have recommended for this exercise.
31 | 5. After you've installed all of the libraries, run `pipenv shell`, which will turn on a virtual environment running Python 3.7.
32 | 6. To exit the pipenv shell when you are done, simply type `exit`.
33 |
34 | ## Instructions
35 |
36 | ### Find a Database
37 | - Before advancing, find a database that you wish to use for a REST API. It helps if the data is something you are interested in, but don't waste too much time on this part. [Kaggle](https://www.kaggle.com/tags/databases) has a great selection of publicly available databases. If you are looking for something specific, Google has a stellar [database search](https://toolbox.google.com/datasetsearch) feature.
38 |
39 | ### Create Your First App
40 |
41 | - Create a Django app in a local directory of your choosing. Feel free to use the [Django tutorial]((https://docs.djangoproject.com/en/2.2/intro/tutorial01/)) to accomplish this, but please don't call your app the standard Polls App. Create a unique application inside of your Django directory to handle your database and models. Make sure the application is configured in your settings.py file!
42 |
43 | ### Create a Django Model
44 | - Create a Django model custom to your database. Feel free to take liberties like creating relational databases for your models. The model field types should match the intended fields of your database. Make sure to migrate your Django model when you are finished!
45 |
46 | ### Configure the REST framework
47 | - Make sure you appropriately configure Django REST Framework in your settings.py file. If you forget this step, Django to recognize the add on.
48 |
49 | ### Serialization
50 | - Before creating a url or view, serialize your data. This allows Django to render data into a JSON format. Make sure you designate the table (model) and fields (features) you wish to include in your REST API.
51 |
52 | ### Create a View
53 | - Use the standard Django REST framework to create your Django view. Django REST framework allows you to interact with your API in both JSON and a preset interactive template. If you feel like going the extra mile, make your database queryable to gather the information you need.
54 |
55 | ### Designating a URL
56 | - Finally, designate url addresses where your page views can be found. Make sure to create a URL scheme that makes sense to how the intended user will interact with your API.
57 |
58 | ### Running your Server
59 | - At this point it is time to test your API. This can be accomplished by the manage.py runserver command. Django's default location is localhost:8000/. From there, follow the naming scheme you created in your urls. Feel free to play with your API by using those filterable features you created!
60 |
61 |
62 | ## Useful Weblinks
63 |
64 |
65 | - Django Startup and Features
66 |
67 | https://docs.djangoproject.com/en/2.2/intro/tutorial01/
68 |
69 | https://docs.djangoproject.com/en/2.2/ref/applications/
70 |
71 | - Django Models
72 |
73 | https://docs.djangoproject.com/en/2.2/ref/models/fields/
74 |
75 | https://docs.djangoproject.com/en/2.2/topics/db/models/#automatic-primary-key-fields
76 |
77 | - Django REST Framework
78 |
79 | https://www.django-rest-framework.org/#installation
80 |
81 | - Serialization
82 |
83 | https://www.django-rest-framework.org/api-guide/serializers/#modelserializer
84 |
85 | https://www.django-rest-framework.org/api-guide/serializers/#specifying-read-only-fields
86 |
87 | - Views and URLS
88 |
89 | https://www.django-rest-framework.org/tutorial/quickstart/#views
90 |
91 | https://www.django-rest-framework.org/tutorial/quickstart/#urls
--------------------------------------------------------------------------------
/problems/webdev/flask_collage/README.md:
--------------------------------------------------------------------------------
1 | Build a small web app using Flask which accepts the meetup.com event id for tonight
2 | as a parameter and would fetch the profile pictures of all the attendees to create a
3 | collage. [Here](https://twitter.com/Tathagata/status/746302962830540801) is an example
4 | of such a collage.
5 |
6 | You'll need:
7 |
8 | - `pip install flask`
9 | - `pip install Flask-WTF`
10 | - `pip install meetup-api`
11 |
12 | How to create a basic Flask app:
13 | Follow the instructions [here](http://flask.pocoo.org/docs/0.11/quickstart/)
14 |
15 | ```python
16 | from flask import Flask
17 | app = Flask(__name__)
18 |
19 | @app.route('/')
20 | def hello_world():
21 | return 'Hello World!'
22 |
23 | if __name__ == '__main__':
24 | app.run(debug=True)
25 | ```
26 | `python testflask.py`
27 |
28 | To get you started, the following piece of code will help you fetch the thumbnail
29 | images from meetup.com.
30 |
31 | ```python
32 | import meetup.api
33 | client = meetup.api.Client('your_key')
34 |
35 | rsvps=client.GetRsvps(event_id='235484841', urlname='_ChiPy_')
36 | member_id = ','.join([str(i['member']['member_id']) for i in rsvps.results])
37 | members = client.GetMembers(member_id=member_id)
38 |
39 | for member in members.results:
40 | try:
41 | print '{0},{1},{2}'.format(member['name'], member['id'], member['photo']['thumb_link'])
42 | except:
43 | pass # ignore those who do not have a complete profile
44 | ```
45 |
46 | 1. Can you include the name along with the images in your collage?
47 |
48 | 2. Add a search box to your collage, where you can search some one by name.
49 | On a successful search, it should display that person and their name. On failure, it should give a proper error message.
50 |
51 | 3. Add the following list of questions to your search result page that collects feedback on what an attendee would like to do
52 |
53 | Choices:
54 | - Help others with Python 101 questions
55 | - Help others with Python Data Science questions
56 | - Help others with Python Web Dev questions
57 | - Python 101 course (Beginner)
58 | - Attend Coding Workshop (Intermediate)
59 | - Attend RasberryPi Lab (Intermediate)
60 | - Work on my own project, get help from others
61 |
62 | 4. Deploy your app to a public hosting, share the link with the world!
63 |
64 | 5. Currently we are accepting RSVPs on both ChiPy's site and Chicago Pythonista's
65 | meetup page. Can you fetch the thumbnails from both the pages, eliminate the
66 | duplicates, and merge them to generate the collage?
67 |
68 |
--------------------------------------------------------------------------------
/problems/webdev/flask_collage/solutions/dormamu_bargain/README.md:
--------------------------------------------------------------------------------
1 | Team Name: Dormamu I've come to bargain
2 | =======================================
3 | * Dr. Strange
4 | * The Ancient One
5 | * Dormammu
6 | * Thanos
7 |
8 | Our Solution
9 | ============
10 | Take over.
11 |
12 |
--------------------------------------------------------------------------------
/problems/webdev/flask_collage/solutions/testflask.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 | app = Flask(__name__)
3 | import meetup.api
4 |
5 | def get_names():
6 | client = meetup.api.Client('3f6d3275d3b6314e73453c4aa27')
7 |
8 | rsvps=client.GetRsvps(event_id='235484841', urlname='_ChiPy_')
9 | member_id = ','.join([str(i['member']['member_id']) for i in rsvps.results])
10 | members = client.GetMembers(member_id=member_id)
11 |
12 | foo=''
13 | for member in members.results:
14 | try:
15 |
16 | foo+='''
17 | {0}
18 | Solo
19 | Team
20 | Line count:
21 |
'''.format(member['name'], member['id'], member['photo']['thumb_link'])
22 | except:
23 | pass # ignore those who do not have a complete profile
24 |
25 | return foo
26 |
27 | @app.route('/')
28 | def hello_world():
29 | html = ''
30 | return html
31 |
32 | if __name__ == '__main__':
33 | app.run(debug=True)
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/problems/webdev/flask_exchange_rates/README.md:
--------------------------------------------------------------------------------
1 | # Mentorship Program Web Project
2 |
3 | ## Objective
4 | All developers in the modern day need to understand web technologies at some level. Whether you're interacting with a Jupyter notebook or querying a web api, understanding how a CLIENT requests information from a SERVER and to see how the SERVER produces its response is incredibly valuable.
5 |
6 |
7 | ## Overview
8 | We will make a web app that serves as both a CLIENT to an external api (exchangeratesapi.io). This app will show conversion rates for currencies, and then add some more complex data.
9 |
10 |
11 | ## Prerequisites
12 | For this project we recommend all use Atom (or Sublime) to write code and a shell/terminal to execute the program. All instructions will be given assuming a Python 3.6 install.
13 |
14 | You should probably have the [Flask documentation](http://flask.pocoo.org/docs/1.0/quickstart/) up as we go through the exercise.
15 |
16 |
17 | ## Initial Setup
18 |
19 | Create a folder for this project: `mkdir mentorship_web && cd mentorship_web`
20 |
21 | If you are using Linux or OS X, run the following to create a new virtualenv
22 |
23 | ```
24 | python3 -m venv venv
25 | source venv/bin/activate
26 | ```
27 |
28 | On Windows, run the following
29 |
30 | ```
31 | python3 -m venv venv
32 | venv\Scripts\activate
33 | ```
34 |
35 | Install Flask, our main web-app: `pip install flask`
36 |
37 | Create a new file called `app.py`
38 |
39 | ```
40 | from flask import Flask
41 | app = Flask(__name__)
42 |
43 |
44 | @app.route('/')
45 | def hello_world():
46 | return "Hello World!"
47 |
48 | if __name__ == '__main__':
49 | app.run(debug=True)
50 | ```
51 |
52 | Run your flask app: `python app.py`
53 |
54 | It should display a link, paste that in your browser to see the running code.
55 |
56 |
57 | ## Display some Exchange Rates
58 |
59 | Define a dictionary with three entries like the following inside your hello route:
60 |
61 | ```
62 | exchange_rates = {
63 | 'EUR': '...',
64 | 'GBP': '...',
65 | '
66 | ```
67 |
68 | Pick any currencies you want! We'll display the values as they convert to USD (because we are in Chicago). Better specify that now for clarity. Add this line just bellow your `app =` definition, we'll use it later:
69 |
70 | ```
71 | BASE_CURRENCY = 'USD'
72 | ```
73 |
74 | Of course, fill in the `...` with real currency values, otherwise our site is pointless! Use a tool like [X-Rates](https://www.x-rates.com/table/?from=USD&amount=1) to look up the currency conversions.
75 |
76 | Now update the return string in the hello route to include the information about these currencies.
77 |
78 |
79 | ## Make it Beautiful
80 | Returning strings is fine...I suppose. But I want big, beautiful HTML! Research Flask's [`render_template` function](http://flask.pocoo.org/docs/1.0/quickstart/#rendering-templates) and add some beautiful looking HTML to format your. Hint: You'll want to create a `templates/` folder in the same directory as your `app.py`, and if you aren't using a kind of loop in your template...it's going to get real tough for you later!
81 |
82 | Test your work by adding another currency to your `exchange_rates` dictionary and watch how your page changes.
83 |
84 | ## Refactor
85 | Define a function right under your `BASE_CURRENCY` definition that looks like the following:
86 |
87 | ```
88 | def exchange_rates():
89 | return # Move your dictionary here
90 | ```
91 |
92 | Then replace any references in your code to use the function instead of defining the dictionary in your route!
93 |
94 | ## Automatic Data
95 | Now comes the fun part. What if we could get the exchange rates on a live feed from a third party service? We can with [ExchangeRatesAPI.io](http://exchangeratesapi.io). This is a free api that runs over simple HTTPS! We'll have something like the following:
96 |
97 | ```
98 | def exchange_rates():
99 | response = requests.get('https://api.exchangeratesapi.io/latest')
100 | return # What are we going to put here now?
101 | ```
102 |
103 | Read up on [Requests' built in JSON Parsing](http://docs.python-requests.org/en/master/user/quickstart/#json-response-content) and try to extract the data from the API response. Your end goal is to return all of the
104 | currencies from this function.
105 |
106 | Hint: This problem is a great time to use the Python debugger ([`pdb`](https://docs.python.org/3/library/pdb.html))! Insert the following line before the `return` command and interact with your server on the command line. So cool!
107 |
108 | ```
109 | import pdb; pdb.set_trace()
110 | ```
111 |
112 | ### Correct Currency
113 | Are we sure the previous step is getting us the currency in our `BASE_CURRENCY` variable? Investigate the [api docs](http://exchangeratesapi.io) to find out how you can change the base currency to the one we want.
114 |
115 |
116 | # You DID it! Now what?
117 | From here on out we are trusting that you can use the documentation, look for resources on your own, and come up with clever solutions to these problems. Try each one, and if you get stuck, move on to a different one! Ask for help if you want more clarification or can't think of a way to do it.
118 |
119 |
120 | ## Information Overload
121 | Could we provide a form that would let people get only the currency they want? I don't need every currency. We'd probably use some kind of html form. And I bet Flask has some documentation on receiving requests from the client.
122 |
123 | e.g. Instead of `JPY->*` I only want to see `USD->GBP` or I only want to see `USD->JPY`.
124 |
125 |
126 | ## Order by Value
127 | How could we order the currencies by the value of the currency?
128 |
129 | e.g. If something is `.00001 USD`, lets list that last, and if something is `987654321 USD` lets list that first.
130 |
131 |
132 | ## User-Specified Base Currency
133 | Some users of our product have complained, rightfully, that they can only get the currency listed in `USD`. We should let them specify what currency they want.
134 |
135 | e.g. Instead of `USD->*` I want to see `CNY->*`
136 |
137 | ### Bonus: Links to each user-specified currency from the currency list
138 | Wouldn't it be great if each currency as it appeared would link to its own currency conversion?
139 |
140 | e.g. From the home page I could click on `JPY` to find out all of the conversions from `JPY->*`.
141 |
142 |
143 | ## Caching
144 | Do we really need to use an API request every time we do a call? How could we store the results of each run to avoid API abuse.
--------------------------------------------------------------------------------
/problems/webdev/flask_team_project/README.md:
--------------------------------------------------------------------------------
1 | In this project we will be building a fully functional web app using
2 | Flask.
3 |
4 | ### The project
5 | In the [team project command line application](https://github.com/chicagopython/CodingWorkshops/tree/master/problems/py101/python_team_project), we built an awesome command line
6 | application for creating teams out of people who have RSVP-ed for a Python Project
7 | Night. However, it is much easier to give a link of your app to someone
8 | than asking them to use a command line. So, we will create a web app, that allows
9 | forming teams from the list of RSVP-s from meetup.com. We
10 | will ask for the number of lines of code that a person has written in
11 | Python or an equivalent language and use it for putting them in a team. The number of lines is just a rough estimate. As a reference, the linux kernel is over 23 million lines of code!
12 |
13 | In short, imagine this as a tool that one of the
14 | organizers uses to checkin attendees as they start coming in on the day of
15 | Project Night.
16 |
17 | Short url for this page: **https://git.io/vdQj6**
18 |
19 | ### Is this project for you
20 | Before you progress further, let's check if we are ready to solve this. You should
21 | - Have a personal computer with working wifi and power cord
22 | - Have Python 3 installed on your computer. Yep, Python 3 only.
23 | - Have [Atom](https://atom.io/) or [Sublime Text](https://www.sublimetext.com/3) installed in your computer.
24 | - Have written & ran programs in Python from the command line
25 | - Have some idea about lists, dictionaries and functions
26 | - Have created a virtual environment and installing packages with `pip`
27 | - You have read the [flask quick introduction](http://flask.pocoo.org/docs/0.12/quickstart/)
28 |
29 | ### What is not supported
30 | This project is not tested using Jupyter Notebook, PyCharm,
31 | Spider, or any other ide/text editor/programming environment for that matter.
32 | Atom or Sublime Text and the command line are the only supported development
33 | environment for this project.
34 |
35 | Sounds good? Then let's dive into building a fully functional web app using flask.
36 |
37 | ### Minimum Viable Product
38 | Our objective is to build an web based interface using Flask that
39 | - Shows a list of people who have RSVP-ed for the project Night
40 | - Each entry in the list should have
41 | - The name of the person
42 | - The meetup.com profile image of the person
43 | - An input text box that allows entering lines of code
44 | - On hitting the submit button we should get teams of four
45 |
46 |
47 | ### Flask
48 | For building the web interface, we will be using Flask.
49 | Flask is a micro web framework - it takes care of handling of the HTTP
50 | protocol for you and allows you focus on your application. It is flexible,
51 | lightweight yet powerful.
52 |
53 | ### Setup your environment
54 | #### Get the source code
55 | - If you are familiar with `git`, run
56 |
57 | git clone https://github.com/chicagopython/CodingWorkshops.git
58 |
59 | - If not, go to https://github.com/chicagopython/CodingWorkshops
60 | - Click on the Download Zip and unzip the file that gets downloaded
61 | - From your command line, change directory to the path where you have downloaded it.
62 | - On linux or OS X
63 |
64 | > cd path/to/CodingWorkshops/problems/webdev/flask_team_project/
65 |
66 | - On Windows
67 |
68 | > cd path\to\CodingWorkshops\problems\webdev\flask_team_project
69 |
70 |
71 | Here you will find the basic skeleton of the app under `app.py`.
72 |
73 | ### Set up virtualenv
74 | If you are using Linux or OS X, run the following to create a new virtualenv
75 |
76 | python3 -m venv venv
77 | source venv/bin/activate
78 | pip install -r requirements.txt
79 | export FLASK_APP=app.py
80 |
81 | On Windows, run the following
82 |
83 | python3 -m venv venv
84 | venv\Scripts\activate
85 | pip install -r requirements.txt
86 | set %FLASK_APP%=app.py
87 |
88 | [](https://asciinema.org/a/M1hP91h153PuOPEjVYbot6jPj)
89 |
90 | ### Feature 0: run app.py
91 | With your environment now set up run
92 |
93 | flask run
94 |
95 | And you'll see 🔥.
96 |
97 | The reason is there is a string in the `app.py` file that allows meetup.com to identify who is trying to get data from them. It is called the API key. The one currently in the code is one of my old ones. You need to get one for your team from [here](https://secure.meetup.com/meetup_api/key/) - obviously, you'll have to be logged into meetup.com to get the key.
98 | Plug in your key whereever most relevant in `app.py` and run the above command again.
99 |
100 | This will start a [web server](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_web_server) on port 5000.
101 | Next load up http://locahost:5000/rsvps in your web browser.
102 |
103 | This will show you the list of people who RSVPed for a previous meetup.
104 | Goto tonight's meetup page and get the meetup id from the url.
105 |
106 | https://www.meetup.com/_ChiPy_/events/244121900/
107 |
108 | The last section of the url is the `event_id`.
109 |
110 | ### Feature 1: Read app.py
111 | `app.py` is the script is where the magic happens.
112 |
113 | Lets start at the routes:
114 |
115 | @app.route('/rsvps')
116 | def rsvps():
117 |
118 |
119 | @app.route('/teams', methods=['GET', 'POST'])
120 | def teams():
121 |
122 | Discuss among the team how render_template function is used in rsvps and teams
123 | function.
124 |
125 | Two useful tools are pretty print and `pdb`
126 |
127 | #### Pretty print
128 |
129 | >> from pprint import pprint as pp
130 | >> pp(member_rsvps)
131 |
132 | This will give you a better view of what the function `get_names()` returns.
133 |
134 | #### pdb
135 | Python comes with a debugger `pdb`. Here's a [cheat sheet](https://appletree.or.kr/quick_reference_cards/Python/Python%20Debugger%20Cheatsheet.pdf)
136 |
137 | You can stick the following line anywhere in the code and make it halt so that you can better inspect the data and flow.
138 |
139 | import pdb; pdb.set_trace()
140 |
141 | ### Feature 2: Show profile images in rsvps
142 | Make changes to rsvps.html (inside templates) to show images of next to the
143 | names of the people.
144 |
145 | ### Feature 3: Add a text box next for lines of code
146 | Add an input type textbox that will take a number as input
147 |
148 | ### Feature 4: Display the lines of code
149 | On hitting submit, the numbers you entered against each person should show up
150 | on the `/teams` page.
151 |
152 | ### Feature 5: Display teams
153 | As of now, everybody is listed under one team: Team 1.
154 | Split the list of people selected into teams of 4
155 |
156 | Your display of each team should include
157 |
158 | Team Number: XYZ
159 | Name of team member1, Lines of code, (pic)
160 | Name of team member2, Lines of code, (pic)
161 | Name of team member3, Lines of code, (pic)
162 | Name of team member4, Lines of code, (pic)
163 | (Total lines of code:)
164 |
165 | where things in () are optional.
166 | There is no specific criteria for creating the teams as of now. We handle that
167 | next.
168 |
169 | ### Feature 6: Tell the world
170 | Record a gif of your app in motion and tweet tweet the link to @chicagopython with "Python Project Night Mentorship". Include the twitter handles of your team members.
171 |
172 | ### Feature 7: Integrate team creating logic (optional)
173 | Code reuse is a hallmark well written code base. Of course, we are
174 | not talking about copy pasting the code, but using the abstractions that a
175 | programming language provides so that there is minimum duplication of code.
176 |
177 | Use the code that you wrote in the team project command line application. The logic
178 | that you have implemented earlier for grouping your list of people into teams
179 | should now be used for creating your teams.
180 |
181 | Thanks! Thats all folks!
182 | If you found a bug or think you some instructions are missing - just open a issue in this repository.
183 |
--------------------------------------------------------------------------------
/problems/webdev/flask_team_project/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, render_template, request
2 | from flask_debugtoolbar import DebugToolbarExtension
3 |
4 | app = Flask(__name__)
5 | app.debug = True
6 | import meetup.api
7 | app.config['SECRET_KEY'] = 'foobar'
8 |
9 | toolbar = DebugToolbarExtension(app)
10 |
11 | def get_names():
12 | client = meetup.api.Client("3f6d3275d3b6314e73453c4aa27")
13 |
14 | rsvps=client.GetRsvps(event_id='235484841', urlname='_ChiPy_')
15 | member_id = ','.join([str(i['member']['member_id']) for i in rsvps.results])
16 | members = client.GetMembers(member_id=member_id)
17 |
18 | foo={}
19 | for member in members.results:
20 | try:
21 | foo[member['name']] = member['photo']['thumb_link']
22 | except:
23 | pass # ignore those who do not have a complete profile
24 | return foo
25 |
26 | member_rsvps=get_names()
27 |
28 | @app.route('/rsvps')
29 | def rsvps():
30 | return render_template('rsvps.html', rsvps=member_rsvps)
31 |
32 |
33 | @app.route('/teams', methods=['GET', 'POST'])
34 | def teams():
35 | results = request.form.to_dict()
36 | return render_template('teams.html', teams=[results])
37 |
38 | if __name__ == '__main__':
39 | app.run(debug=True)
40 |
--------------------------------------------------------------------------------
/problems/webdev/flask_team_project/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask==0.12.2
2 | meetup-api==0.1.1
3 | Flask-WTF==0.14.2
4 | flask-debugtoolbar==0.10.1
5 |
--------------------------------------------------------------------------------
/problems/webdev/flask_team_project/templates/rsvps.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/problems/webdev/flask_trackcoder/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | - [1. Flask Dashboard for tracking developer time](#1-flask-dashboard-for-tracking-developer-time)
4 | - [1.1. Is this project for you](#11-is-this-project-for-you)
5 | - [1.2. Reference documents](#12-reference-documents)
6 | - [1.3. What is not supported](#13-what-is-not-supported)
7 | - [1.4. Minimum Viable Product](#14-minimum-viable-product)
8 | - [1.5. Flask](#15-flask)
9 | - [1.6. Setup your environment](#16-setup-your-environment)
10 | - [1.6.1. Get the source code](#161-get-the-source-code)
11 | - [1.7. Files](#17-files)
12 | - [1.8. Set up virtualenv](#18-set-up-virtualenv)
13 | - [1.9. run app.py](#19-run-apppy)
14 | - [1.10. Shows the list of tasks that a mentee has added](#110-shows-the-list-of-tasks-that-a-mentee-has-added)
15 | - [1.11. Shows the list of tasks of a particular type](#111-shows-the-list-of-tasks-of-a-particular-type)
16 | - [1.12. Add a form to search for tasks](#112-add-a-form-to-search-for-tasks)
17 | - [1.13. Add a from to add new Task from the UI](#113-add-a-from-to-add-new-task-from-the-ui)
18 | - [1.14. Show the total time spent by the mentee for each of the task types](#114-show-the-total-time-spent-by-the-mentee-for-each-of-the-task-types)
19 | - [1.15. Improve the UI](#115-improve-the-ui)
20 | - [1.16. Integrate the task filtering with these aggregate metrics](#116-integrate-the-task-filtering-with-these-aggregate-metrics)
21 | - [1.17. Show the list of hashtags and their corresponding counts](#117-show-the-list-of-hashtags-and-their-corresponding-counts)
22 | - [Feedback](#feedback)
23 |
24 |
25 | # 1. Flask Dashboard for tracking developer time
26 |
27 | Chipy's mentorship program is an extra-ordinary jounery for becoming a better developer. As a mentee, you are expected to do a lot - you read new articles/books, write code, debug and troubleshoot, pair program with other mentees in coding workshop or your mentor. This is involves managing time efficiently and doing the effective things. But as the old adage goes, "you can't manage what you can't measure".
28 |
29 | This project is the second of the three part series of building tools for the mentees for tracking time. The end goal of such a tool will be to aggregate anonymous data and analyze how does a typical mentee spend on blogging (b), coding (c), debugging (d), pair program (p) with mentor or other mentees.
30 |
31 | In this project we will be building a fully functional web dashboard for tracking developer efforts using Flask.
32 |
33 | Short url for this page: **http://bit.ly/flask_trackcoder**
34 |
35 | ## 1.1. Is this project for you
36 |
37 | Before you progress further, let's check if we are ready to solve this. You should
38 |
39 | - Have a personal computer with working wifi and power cord
40 | - Have Python 3 installed on your computer. Yep, Python 3 only
41 | - Have [Atom](https://atom.io/) or [Sublime Text](https://www.sublimetext.com/3) installed in your computer
42 | - Have written & ran programs in Python from the command line
43 | - Have some idea about lists, dictionaries and functions
44 | - Have created a virtual environment and installing packages with `pip`
45 | - You have read the [flask quick introduction](http://flask.pocoo.org/docs/0.12/quickstart/)
46 |
47 | In addition, you should be familiar with [Part 1](https://github.com/chicagopython/CodingWorkshops/tree/master/problems/py101/trackcoder) of this three part exercise.
48 |
49 | ## 1.2. Reference documents
50 |
51 | Reading these links before attending project night, would help you a lot by providing
52 | the background needed to work through the exercieses.
53 |
54 | - [What is a Web server](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_web_server)
55 | - [Flask Quick Start](http://flask.pocoo.org/docs/1.0/quickstart/)
56 | - [Flask routing](http://flask.pocoo.org/snippets/57/)
57 | - [Flask Web Forms](https://pythonspot.com/flask-web-forms/)
58 | - [Flask Context processors](http://flask.pocoo.org/docs/1.0/templating/#context-processors)
59 | - [Aggregation in Peewee ORM](http://docs.peewee-orm.com/en/latest/peewee/query_examples.html#aggregation)
60 |
61 | ## 1.3. What is not supported
62 |
63 | This project is not tested using Jupyter Notebook, PyCharm,
64 | Spider, or any other ide/text editor/programming environment for that matter.
65 | Atom or Sublime Text and the command line are the only supported development
66 | environment for this project.
67 |
68 | Sounds good? Then let's dive into building a fully functional web app using flask.
69 |
70 | ## 1.4. Minimum Viable Product
71 |
72 | Our objective is to build a dashboard that tells the story of the progress
73 | of a mentee during the mentorship program. The key metrics on this dashboard
74 | comes from data captured by them using the command line tool built in Part
75 | 1 of this project. We want to get insights on the time spent in each task type,
76 | and the effect of each task on the entire progress. Later in Part 3 we will use
77 | data science approaches to analyze any patterns in this data.
78 |
79 | 
80 |
81 | This is just a reference implementation to give you an idea of what a dashboard
82 | might look like, but you are not required to stick to this and encouraged to
83 | think of your own design.
84 |
85 | ## 1.5. Flask
86 |
87 | For building the web interface, we will be using Flask.
88 | Flask is a micro web framework - it takes care of handling of the HTTP
89 | protocol for you and allows you focus on your application. It is flexible,
90 | lightweight yet powerful.
91 |
92 | ## 1.6. Setup your environment
93 |
94 | ### 1.6.1. Get the source code
95 |
96 | If you are familiar with `git`, run
97 |
98 | git clone https://github.com/chicagopython/CodingWorkshops.git
99 |
100 | - If not, go to https://github.com/chicagopython/CodingWorkshops
101 | - Click on the Download Zip and unzip the file that gets downloaded
102 | - From your command line, change directory to the path where you have downloaded it.
103 | - On linux or OS X
104 |
105 | > cd path/to/CodingWorkshops/problems/webdev/flask_trackcoder/
106 |
107 | - On Windows
108 |
109 | > cd path\to\CodingWorkshops\problems\webdev\flask_trackcoder
110 |
111 | ## 1.7. Files
112 |
113 | - `app.py` - Here you will find the basic skeleton of the flask app. This is where you will be
114 | writing your python code for controlling the business logic of the problem.
115 | - `static/styles` - If you want to add static css and javascript, that goes here. [Docs](http://flask.pocoo.org/docs/1.0/tutorial/static/)
116 | - `templates` - This folder has the file `tasks.html` which serves as the view for your app, rendering the data on the browser. [Docs](http://flask.pocoo.org/docs/1.0/tutorial/templates/)
117 |
118 | ## 1.8. Set up virtualenv
119 |
120 | If you are using Linux or OS X, run the following to create a new virtualenv
121 |
122 | python3 -m venv venv
123 | source venv/bin/activate
124 | pip install -r requirements.txt
125 | export FLASK_APP=app.py
126 |
127 | On Windows, run the following
128 |
129 | python3 -m venv venv
130 | venv\Scripts\activate
131 | pip install -r requirements.txt
132 | set %FLASK_APP%=app.py
133 |
134 | ## 1.9. run app.py
135 |
136 | With your environment now set up run
137 |
138 | flask run
139 |
140 | This will start a web server on port 5000.
141 | Next load up http://localhost:5000/tasks in your web browser.
142 |
143 | This will show you the list of tasks that have been added to the database built and provided
144 | in Part 1 of our project.
145 |
146 | ## 1.10. Shows the list of tasks that a mentee has added
147 |
148 | `app.py` is the script is where the magic happens.
149 |
150 | Lets start with the routes:
151 |
152 |
153 | @app.route('/tasks/', methods=['GET', 'POST'], defaults={'task':''})
154 | @app.route('/tasks/', methods=['GET', 'POST'])
155 | def tasks(task):
156 | tasks = [t for t in ToDo.select()]
157 | return render_template('tasks.html', tasks=tasks)
158 |
159 | Visit http://localhost:5000/tasks and you will see all the tasks.
160 | How many tasks do you see?
161 |
162 | Feel free to add your own tasks
163 |
164 | cd ../../py101/trackcoder/ # change directory to go to Part 1
165 | python app.py -a d 5 "#bootstrap trying to get form alignment working"
166 |
167 | Now if you refresh the page, you should be able to see the tasks you added on
168 | the dashboard.
169 |
170 | ## 1.11. Shows the list of tasks of a particular type
171 |
172 | Recall that we have the following task types.
173 |
174 | - blogging (b)
175 | - coding (c)
176 | - debugging (d)
177 | - pair programming at project night (p)
178 | - research (r)
179 | - meeting with mentor (m)
180 |
181 | Change the tasks route so that it takes a parameter of task type and filters
182 | out tasks of only that type. In absence of any task type, we should be
183 | able to see all the tasks.
184 |
185 | Note: The data comes from database which is located in the folder
186 | '../../py101/trackcoder/to_do_list.db'. Changing it to another sqlite file,
187 | would change what you see on the dashboard.
188 |
189 | Think how will you handle if an invalid task type is requested.
190 |
191 | ## 1.12. Add a form to search for tasks
192 |
193 | Add a search text box that allows the user to search with any word that might
194 | appear on the description of the task.
195 |
196 | Hint: Take a look at the example of Flask-WTF above. You'll also need to take
197 | a look at [how to do substring search](http://docs.peewee-orm.com/en/latest/peewee/query_operators.html?highlight=contains) usnig peewee.
198 |
199 | ## 1.13. Add a from to add new Task from the UI
200 |
201 | Add a form to the UI so that you can add a task from the web frontend.
202 | The task will take in three parameters
203 |
204 | - a task type
205 | - minutes spent
206 | - description
207 |
208 | Think about the UI from the user's perspective. Instead of having to fill in three fields
209 | you may choose to have one single text box, where they can enter
210 |
211 | d, 30, #flask form submission
212 |
213 | and you handle splitting the input into task type, minutes, description.
214 |
215 | ## 1.14. Show the total time spent by the mentee for each of the task types
216 |
217 | Find the cummulative time spent on each task type. These should be
218 | placed next to each other so that it is easy to compare how much time
219 | is devoted into each of the activity.
220 |
221 | Hint: While there might be different ways of doing this, you might find [context processors](http://flask.pocoo.org/docs/1.0/templating/#context-processors)
222 | useful.
223 |
224 | ## 1.15. Improve the UI
225 |
226 | UI/UX is critical to keep your audience engaged. The sample template `tasks.html`, comes with
227 | bootstap integrated, however it does not use any additional styling yet.
228 |
229 | Here is the template from Bootrap that is used in the demo above.
230 | https://getbootstrap.com/docs/4.0/examples/dashboard/. It can be downloaded from https://getbootstrap.com/docs/4.0/examples/.
231 |
232 | Feel free to go with any styling that goes with your team's design.
233 |
234 | Your html template should go to `template` directory and the `css` or `js`
235 | files should go into `static/styles` directory.
236 |
237 | ## 1.16. Integrate the task filtering with these aggregate metrics
238 |
239 | Now that you have a way to filter by task type and aggregated metrics on each task type, integrate these to provide a more unified user experience. Take a look at the demo above, on how it uses the cards almost as a button to filter each task type. Feel free to come up with your own design.
240 |
241 | Hint: Cards are a very common way of displaying aggregated information and Bootstrap natively supports cards. You can see the examples [here](https://getbootstrap.com/docs/4.0/components/card/).
242 |
243 | ## 1.17. Show the list of hashtags and their corresponding counts
244 |
245 | Adding hashtags on task descriptions allows aggregating the efforts
246 | on another dimension than the pre-defined task types. For example, if
247 | you want to see how much time was spent on debugging on
248 | "#data science" vs "asyncio", aggregating on tasks that have those
249 | hashtags is an easy way.
250 |
251 | Provide a way to aggregate the hashtags that has been used in the tasks
252 | and show the count next to each of them. Clicking on a particlar
253 | hashtag should show only the tasks that have that hashtag.
254 |
255 | # Feedback
256 |
257 | Thanks for attending this project night. We put in a lot of effor to
258 | make this useful for you. However, we can not make it better, unless
259 | we hear back from you on what you want and collect data to make changes.
260 | Please take a few moments to fill in the small form below and help us improve it.
261 |
262 | [](https://docs.google.com/forms/d/e/1FAIpQLSePDQlWOibJrF7rI5KrYhzUSNfXp9GMP-6b-bjC8_qSFgYp-w/viewform?usp=pp_url&entry.813953991=https://bit.ly/flask_project)
263 |
--------------------------------------------------------------------------------
/problems/webdev/flask_trackcoder/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, flash, redirect, render_template, \
2 | request, url_for
3 | from peewee import *
4 | import datetime
5 | import logging
6 | import re
7 | from collections import Counter
8 | from wtforms import Form, StringField
9 |
10 | logger = logging.getLogger(__name__)
11 |
12 | app = Flask(__name__)
13 | app.debug = True
14 | app.config['SECRET_KEY'] = 'foobar'
15 |
16 | db = SqliteDatabase('../../py101/trackcoder/to_do_list.db')
17 |
18 | class ToDo(Model):
19 | task = CharField(max_length=255)
20 | description = CharField(max_length=255)
21 | timestamp = DateTimeField(default=datetime.datetime.now)
22 | mins = IntegerField()
23 | done = BooleanField(default=True)
24 |
25 | class Meta:
26 | database = db
27 |
28 | @app.route('/tasks/', methods=['GET', 'POST'], defaults={'tasks':None})
29 | def tasks(tasks):
30 | tasks = [t for t in ToDo.select()]
31 | return render_template('tasks.html', tasks=tasks)
32 |
33 | if __name__ == '__main__':
34 | app.run(debug=True)
35 |
--------------------------------------------------------------------------------
/problems/webdev/flask_trackcoder/dashboard.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chicagopython/CodingWorkshops/45c00db2e154ed164b5299ed9794f721208ab3fb/problems/webdev/flask_trackcoder/dashboard.gif
--------------------------------------------------------------------------------
/problems/webdev/flask_trackcoder/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask
2 | peewee
3 | prompt_toolkit
4 | click
5 | Flask-WTF
--------------------------------------------------------------------------------
/problems/webdev/flask_trackcoder/static/styles/dashboard.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-size: .875rem;
3 | }
4 |
5 | .feather {
6 | width: 16px;
7 | height: 16px;
8 | vertical-align: text-bottom;
9 | }
10 |
11 | /*
12 | * Sidebar
13 | */
14 |
15 | .sidebar {
16 | position: fixed;
17 | top: 0;
18 | bottom: 0;
19 | left: 0;
20 | z-index: 100; /* Behind the navbar */
21 | padding: 0;
22 | box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
23 | }
24 |
25 | .sidebar-sticky {
26 | position: -webkit-sticky;
27 | position: sticky;
28 | top: 48px; /* Height of navbar */
29 | height: calc(100vh - 48px);
30 | padding-top: .5rem;
31 | overflow-x: hidden;
32 | overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
33 | }
34 |
35 | .sidebar .nav-link {
36 | font-weight: 500;
37 | color: #333;
38 | }
39 |
40 | .sidebar .nav-link .feather {
41 | margin-right: 4px;
42 | color: #999;
43 | }
44 |
45 | .sidebar .nav-link.active {
46 | color: #007bff;
47 | }
48 |
49 | .sidebar .nav-link:hover .feather,
50 | .sidebar .nav-link.active .feather {
51 | color: inherit;
52 | }
53 |
54 | .sidebar-heading {
55 | font-size: .75rem;
56 | text-transform: uppercase;
57 | }
58 |
59 | /*
60 | * Navbar
61 | */
62 |
63 | .navbar-brand {
64 | padding-top: .75rem;
65 | padding-bottom: .75rem;
66 | font-size: 1rem;
67 | background-color: rgba(0, 0, 0, .25);
68 | box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
69 | }
70 |
71 | .navbar .form-control {
72 | padding: .75rem 1rem;
73 | border-width: 0;
74 | border-radius: 0;
75 | }
76 |
77 | .form-control-dark {
78 | color: #fff;
79 | background-color: rgba(255, 255, 255, .1);
80 | border-color: rgba(255, 255, 255, .1);
81 | }
82 |
83 | .form-control-dark:focus {
84 | border-color: transparent;
85 | box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
86 | }
87 |
88 | /*
89 | * Utilities
90 | */
91 |
92 | .border-top { border-top: 1px solid #e5e5e5; }
93 | .border-bottom { border-bottom: 1px solid #e5e5e5; }
94 |
95 | a.custom-card,
96 | a.custom-card:hover {
97 | color: inherit;
98 | }
--------------------------------------------------------------------------------
/problems/webdev/flask_trackcoder/templates/tasks.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Dashboard Template for Bootstrap
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |