├── .gitignore
├── Makefile
├── README.rst
├── docs
└── LICENSE.txt
├── docutils.conf
├── make.bat
├── notebooks
└── Networking & Sockets.ipynb
├── requirements.pip
├── resources
├── session01
│ ├── echo_client.py
│ ├── echo_server.py
│ ├── socket_tools.py
│ ├── tasks.txt
│ └── tests.py
├── session02
│ ├── homework
│ │ ├── http_server.py
│ │ ├── simple_client.py
│ │ ├── tests.py
│ │ └── webroot
│ │ │ ├── a_web_page.html
│ │ │ ├── images
│ │ │ ├── JPEG_example.jpg
│ │ │ ├── Sample_Scene_Balls.jpg
│ │ │ └── sample_1.png
│ │ │ ├── make_time.py
│ │ │ └── sample.txt
│ ├── http_server.py
│ ├── simple_client.py
│ └── tests.py
├── session03
│ ├── cgi
│ │ ├── cgi-bin
│ │ │ ├── cgi_1.py
│ │ │ ├── cgi_2.py
│ │ │ └── cgi_sums.py
│ │ └── index.html
│ ├── http_server.py
│ └── wsgi
│ │ ├── bookapp.py
│ │ ├── bookdb.py
│ │ ├── tests.py
│ │ └── wsgi_1.py
├── session04
│ ├── mashup_1.py
│ ├── mashup_2.py
│ ├── mashup_3.py
│ ├── mashup_4.py
│ └── mashup_5.py
├── session06
│ ├── __init__.py
│ ├── development.ini
│ ├── forms.py
│ ├── layout.jinja2
│ ├── learning_journal
│ │ ├── .gitignore
│ │ ├── CHANGES.txt
│ │ ├── MANIFEST.in
│ │ ├── README.txt
│ │ ├── development.ini
│ │ ├── learning_journal
│ │ │ ├── __init__.py
│ │ │ ├── forms.py
│ │ │ ├── models.py
│ │ │ ├── scripts
│ │ │ │ ├── __init__.py
│ │ │ │ └── initializedb.py
│ │ │ ├── static
│ │ │ │ ├── pyramid-16x16.png
│ │ │ │ ├── pyramid.png
│ │ │ │ ├── styles.css
│ │ │ │ ├── theme.css
│ │ │ │ └── theme.min.css
│ │ │ ├── templates
│ │ │ │ ├── detail.jinja2
│ │ │ │ ├── edit.jinja2
│ │ │ │ ├── layout.jinja2
│ │ │ │ ├── list.jinja2
│ │ │ │ └── mytemplate.pt
│ │ │ ├── tests.py
│ │ │ └── views.py
│ │ ├── production.ini
│ │ └── setup.py
│ ├── models.py
│ └── styles.css
├── session07
│ ├── detail.jinja2
│ ├── forms.py
│ ├── learning_journal
│ │ ├── .gitignore
│ │ ├── CHANGES.txt
│ │ ├── MANIFEST.in
│ │ ├── Procfile
│ │ ├── README.txt
│ │ ├── build_db
│ │ ├── development.ini
│ │ ├── learning_journal
│ │ │ ├── __init__.py
│ │ │ ├── forms.py
│ │ │ ├── models.py
│ │ │ ├── scripts
│ │ │ │ ├── __init__.py
│ │ │ │ └── initializedb.py
│ │ │ ├── security.py
│ │ │ ├── static
│ │ │ │ ├── pyramid-16x16.png
│ │ │ │ ├── pyramid.png
│ │ │ │ ├── styles.css
│ │ │ │ ├── theme.css
│ │ │ │ └── theme.min.css
│ │ │ ├── templates
│ │ │ │ ├── detail.jinja2
│ │ │ │ ├── edit.jinja2
│ │ │ │ ├── layout.jinja2
│ │ │ │ ├── list.jinja2
│ │ │ │ └── mytemplate.pt
│ │ │ ├── tests.py
│ │ │ └── views.py
│ │ ├── production.ini
│ │ ├── requirements.txt
│ │ ├── run
│ │ ├── runapp.py
│ │ ├── runtime.txt
│ │ └── setup.py
│ ├── models.py
│ └── views.py
├── session08
│ ├── django_blog.css
│ ├── myblog_test_fixture.json
│ ├── mysite_stage_1
│ │ ├── manage.py
│ │ ├── myblog
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── apps.py
│ │ │ ├── fixtures
│ │ │ │ └── myblog_test_fixture.json
│ │ │ ├── migrations
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_category.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── tests.py
│ │ │ └── views.py
│ │ └── mysite
│ │ │ ├── __init__.py
│ │ │ ├── settings.py
│ │ │ ├── urls.py
│ │ │ └── wsgi.py
│ ├── mysite_stage_2
│ │ ├── manage.py
│ │ ├── myblog
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── apps.py
│ │ │ ├── fixtures
│ │ │ │ └── myblog_test_fixture.json
│ │ │ ├── migrations
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_category.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── templates
│ │ │ │ └── list.html
│ │ │ ├── tests.py
│ │ │ ├── urls.py
│ │ │ └── views.py
│ │ └── mysite
│ │ │ ├── __init__.py
│ │ │ ├── settings.py
│ │ │ ├── templates
│ │ │ └── base.html
│ │ │ ├── urls.py
│ │ │ └── wsgi.py
│ └── mysite_stage_3
│ │ ├── manage.py
│ │ ├── myblog
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── fixtures
│ │ │ └── myblog_test_fixture.json
│ │ ├── migrations
│ │ │ ├── 0001_initial.py
│ │ │ ├── 0002_category.py
│ │ │ └── __init__.py
│ │ ├── models.py
│ │ ├── static
│ │ │ └── django_blog.css
│ │ ├── templates
│ │ │ ├── detail.html
│ │ │ └── list.html
│ │ ├── tests.py
│ │ ├── urls.py
│ │ └── views.py
│ │ └── mysite
│ │ ├── __init__.py
│ │ ├── settings.py
│ │ ├── templates
│ │ ├── base.html
│ │ └── login.html
│ │ ├── urls.py
│ │ └── wsgi.py
└── session09
│ └── mysite
│ ├── manage.py
│ ├── myblog
│ ├── __init__.py
│ ├── admin.py
│ ├── fixtures
│ │ └── myblog_test_fixture.json
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_category.py
│ │ └── __init__.py
│ ├── models.py
│ ├── static
│ │ └── django_blog.css
│ ├── templates
│ │ ├── detail.html
│ │ └── list.html
│ ├── tests.py
│ ├── urls.py
│ └── views.py
│ └── mysite
│ ├── __init__.py
│ ├── settings.py
│ ├── templates
│ ├── base.html
│ └── login.html
│ ├── urls.py
│ └── wsgi.py
└── source
├── _static
├── admin_index.png
├── apache.png
├── bike.jpg
├── bluebox_logo.png
├── by-nc-sa.png
├── cgitb_output.png
├── cloud_cover.jpg
├── custom.css
├── data_flow.png
├── data_in_tcpip_stack.png
├── django-admin-login.png
├── django-pony.png
├── django-start.png
├── django_lead.png
├── flask_cover.png
├── flask_full.png
├── flask_hello.png
├── flask_square.png
├── forbidden.png
├── framework_quote.png
├── gateway.jpg
├── geojson-io.png
├── granny_mashup.png
├── heroku-logo.png
├── icup.png
├── learning_journal_styled.png
├── lj_entry.png
├── logo_UW.png
├── mac-icon.png
├── mod_wsgi_flow.png
├── network_topology.png
├── nginx.png
├── nginx_hello.png
├── no_entry.jpg
├── plone-icon-256-white-bg.png
├── plone_conf_2012.jpg
├── protocol.png
├── protocol_sea.png
├── proxy_wsgi.png
├── pyramid-medium.png
├── python.png
├── scream.jpg
├── sheep_pyramid.jpg
├── skateboard.jpg
├── socket_interaction.png
├── wiki_frontpage.png
├── wsgi_middleware_onion.png
└── wsgiref_flow.png
├── _templates
├── end_slide.html
└── title_slide.html
├── _themes
├── uwpce_slides2
│ ├── end_slide.html
│ ├── layout.html
│ ├── slide.html
│ ├── static
│ │ ├── .sass-cache
│ │ │ ├── 0fe24dfc41fffed2d6891c797fcd7dee100afa65
│ │ │ │ └── _hacks.scssc
│ │ │ ├── 17d03613c125918cd0766f51918feb21dc3c074a
│ │ │ │ ├── _background-size.scssc
│ │ │ │ ├── _border-radius.scssc
│ │ │ │ ├── _box-shadow.scssc
│ │ │ │ ├── _box-sizing.scssc
│ │ │ │ ├── _box.scssc
│ │ │ │ ├── _columns.scssc
│ │ │ │ ├── _deprecated-support.scssc
│ │ │ │ ├── _images.scssc
│ │ │ │ ├── _text-shadow.scssc
│ │ │ │ ├── _transform.scssc
│ │ │ │ ├── _transition.scssc
│ │ │ │ └── _user-interface.scssc
│ │ │ ├── 65e4c30c131f260ea88c3e4f2e16dfc2ba547e74
│ │ │ │ └── _utilities.scssc
│ │ │ ├── a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783
│ │ │ │ ├── _base.scssc
│ │ │ │ ├── _variables.scssc
│ │ │ │ ├── default.scssc
│ │ │ │ ├── hieroglyph.scssc
│ │ │ │ ├── io2013.scssc
│ │ │ │ └── phone.scssc
│ │ │ └── af1a5722249b61fe97c5776f7a26e0902a00f406
│ │ │ │ ├── _reset.scssc
│ │ │ │ └── _support.scssc
│ │ ├── README.md
│ │ ├── config.rb
│ │ ├── js
│ │ │ ├── hammer.js
│ │ │ ├── modernizr.custom.45394.js
│ │ │ ├── order.js
│ │ │ ├── polyfills
│ │ │ │ ├── classList.min.js
│ │ │ │ ├── dataset.min.js
│ │ │ │ └── history.min.js
│ │ │ ├── prettify
│ │ │ │ ├── lang-apollo.js
│ │ │ │ ├── lang-clj.js
│ │ │ │ ├── lang-css.js
│ │ │ │ ├── lang-go.js
│ │ │ │ ├── lang-hs.js
│ │ │ │ ├── lang-lisp.js
│ │ │ │ ├── lang-lua.js
│ │ │ │ ├── lang-ml.js
│ │ │ │ ├── lang-n.js
│ │ │ │ ├── lang-proto.js
│ │ │ │ ├── lang-scala.js
│ │ │ │ ├── lang-sql.js
│ │ │ │ ├── lang-tex.js
│ │ │ │ ├── lang-vb.js
│ │ │ │ ├── lang-vhdl.js
│ │ │ │ ├── lang-wiki.js
│ │ │ │ ├── lang-xq.js
│ │ │ │ ├── lang-yaml.js
│ │ │ │ ├── prettify.css
│ │ │ │ └── prettify.js
│ │ │ ├── require-1.0.8.min.js
│ │ │ ├── slide-controller.js
│ │ │ ├── slide-deck-instantiate.js
│ │ │ ├── slide-deck.js
│ │ │ ├── slide-testing.js
│ │ │ └── slides.js
│ │ ├── scripts
│ │ │ └── md
│ │ │ │ ├── README.md
│ │ │ │ ├── base.html
│ │ │ │ ├── render.py
│ │ │ │ └── slides.md
│ │ ├── slide_config.js
│ │ ├── slide_config.js_t
│ │ ├── template.html
│ │ └── theme
│ │ │ ├── css
│ │ │ ├── default.css
│ │ │ ├── hieroglyph.css
│ │ │ ├── io2013.css
│ │ │ └── phone.css
│ │ │ └── scss
│ │ │ ├── _base.scss
│ │ │ ├── _variables.scss
│ │ │ ├── default.scss
│ │ │ ├── hieroglyph.scss
│ │ │ ├── io2013.scss
│ │ │ └── phone.scss
│ ├── theme.conf
│ └── title_slide.html
└── uwpce_theme
│ ├── layout.html
│ ├── static
│ ├── dialog-note.png
│ ├── dialog-seealso.png
│ ├── dialog-todo.png
│ ├── dialog-topic.png
│ ├── dialog-warning.png
│ ├── epub.css
│ ├── footerbg.png
│ ├── headerbg.png
│ ├── ie6.css
│ ├── middlebg.png
│ ├── pyramid.css_t
│ └── transparent.gif
│ └── theme.conf
├── conf.py
├── index.rst
├── outline.rst
├── presentations
├── django_intro.rst
├── index.rst
├── session01.rst
├── session02.rst
├── session03.rst
├── session04.rst
├── session05.rst
├── session06.rst
├── session07.rst
├── session08.rst
├── session09.rst
├── session10.rst
└── venv_intro.rst
└── readings.rst
/.gitignore:
--------------------------------------------------------------------------------
1 | .svn
2 | .DS_Store
3 | *.pyc
4 | *.pyo
5 | *.egg-info
6 | *.egg
7 | *.pt.cache
8 | .Python
9 | *.mo
10 | *.o
11 | *.lo
12 | *.la
13 | .*.rej
14 | *.rej
15 | .*~
16 | *~
17 | #*#
18 | .#*
19 | .~lock.*#
20 | .VimballRecord
21 | .netrwhist
22 | tags
23 | tags-*
24 | en.utf-8.add.spl
25 | svn-commit.tmp
26 | .AppleDouble
27 | .vim/bundle/command-t/ruby/command-t/Makefile
28 | .vim/bundle/command-t/ruby/command-t/ext.bundle
29 | .vim/bundle/command-t/ruby/command-t/mkmf.log
30 | .installed.cfg
31 | bin
32 | build
33 | include
34 | lib
35 | share
36 | cast-offs
37 | develop-eggs
38 | development
39 | *.db
40 | *.sublime-project
41 | *.sublime-workspace
42 | .mr.developer.cfg
43 | outline_improvements.txt
44 | src
45 | html
46 | slides
47 | new_mash
48 | .buildinfo
49 | pip-selfcheck.json
50 | .ipynb_checkpoints
51 | testenvs
52 |
--------------------------------------------------------------------------------
/docutils.conf:
--------------------------------------------------------------------------------
1 | [general]
2 | source_url: http://github.com/cewing/training.python_web
3 |
4 | [restructuredtext parser]
5 | syntax_highlight = short
6 |
7 | [s5_html writer]
8 | current_slide: True
9 | embed_stylesheet: false
10 | stylesheet: ui/uw_pce_theme/pretty.css
11 | theme_url: ui/uw_pce_theme
12 |
--------------------------------------------------------------------------------
/requirements.pip:
--------------------------------------------------------------------------------
1 | alabaster==0.7.6
2 | appnope==0.1.0
3 | Babel==2.0
4 | decorator==4.0.2
5 | docutils==0.12
6 | gnureadline==6.3.3
7 | hieroglyph==0.7.1
8 | ipython==4.0.0
9 | ipython-genutils==0.1.0
10 | Jinja2==2.8
11 | MarkupSafe==0.23
12 | path.py==8.1
13 | pexpect==3.3
14 | pickleshare==0.5
15 | py==1.4.30
16 | Pygments==2.0.2
17 | pytest==2.7.2
18 | pytz==2015.4
19 | simplegeneric==0.8.1
20 | six==1.9.0
21 | snowballstemmer==1.2.0
22 | Sphinx==1.3.1
23 | sphinx-rtd-theme==0.1.8
24 | traitlets==4.0.0
25 |
--------------------------------------------------------------------------------
/resources/session01/echo_client.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 |
4 |
5 | def client(msg, log_buffer=sys.stderr):
6 | server_address = ('localhost', 10000)
7 | # TODO: Replace the following line with your code which will instantiate
8 | # a TCP socket with IPv4 Addressing, call the socket you make 'sock'
9 | sock = None
10 | print('connecting to {0} port {1}'.format(*server_address), file=log_buffer)
11 | # TODO: connect your socket to the server here.
12 |
13 | # you can use this variable to accumulate the entire message received back
14 | # from the server
15 | received_message = ''
16 |
17 | # this try/finally block exists purely to allow us to close the socket
18 | # when we are finished with it
19 | try:
20 | print('sending "{0}"'.format(msg), file=log_buffer)
21 | # TODO: send your message to the server here.
22 |
23 | # TODO: the server should be sending you back your message as a series
24 | # of 16-byte chunks. Accumulate the chunks you get to build the
25 | # entire reply from the server. Make sure that you have received
26 | # the entire message and then you can break the loop.
27 | #
28 | # Log each chunk you receive. Use the print statement below to
29 | # do it. This will help in debugging problems
30 | chunk = ''
31 | print('received "{0}"'.format(chunk.decode('utf8')), file=log_buffer)
32 | finally:
33 | # TODO: after you break out of the loop receiving echoed chunks from
34 | # the server you will want to close your client socket.
35 | print('closing socket', file=log_buffer)
36 |
37 | # TODO: when all is said and done, you should return the entire reply
38 | # you received from the server as the return value of this function.
39 |
40 |
41 | if __name__ == '__main__':
42 | if len(sys.argv) != 2:
43 | usage = '\nusage: python echo_client.py "this is my message"\n'
44 | print(usage, file=sys.stderr)
45 | sys.exit(1)
46 |
47 | msg = sys.argv[1]
48 | client(msg)
49 |
--------------------------------------------------------------------------------
/resources/session01/socket_tools.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 |
4 | def get_constants(prefix):
5 | return {getattr(socket, n): n for n in dir(socket) if n.startswith(prefix)}
6 |
7 |
8 | families = get_constants('AF_')
9 | types = get_constants('SOCK_')
10 | protocols = get_constants('IPPROTO_')
11 |
12 |
13 | def get_address_info(host, port):
14 | for response in socket.getaddrinfo(host, port):
15 | fam, typ, pro, nam, add = response
16 | print('family: {}'.format(families[fam]))
17 | print('type: {}'.format(types[typ]))
18 | print('protocol: {}'.format(protocols[pro]))
19 | print('canonical name: {}'.format(nam))
20 | print('socket address: {}'.format(add))
21 | print()
22 |
--------------------------------------------------------------------------------
/resources/session01/tasks.txt:
--------------------------------------------------------------------------------
1 | Session 4 Homework
2 | ==================
3 |
4 | Required Tasks:
5 | ---------------
6 |
7 | * Complete the code in ``echo_server.py`` to create a server that sends back
8 | whatever messages it receives from a client
9 |
10 | * Complete the code in ``echo_client.py`` to create a client function that
11 | can send a message and receive a reply.
12 |
13 | * Ensure that the tests in ``tests.py`` pass.
14 |
15 | To run the tests:
16 |
17 | * Open one terminal while in this folder and execute this command:
18 |
19 | $ python echo_server.py
20 |
21 | * Open a second terminal in this same folder and execute this command:
22 |
23 | $ python tests.py
24 |
25 |
26 |
27 |
28 | Optional Tasks:
29 | ---------------
30 |
31 | Simple:
32 |
33 | * Write a python function that lists the services provided by a given range of
34 | ports.
35 |
36 | * accept the lower and upper bounds as arguments
37 | * provide sensible defaults
38 | * Ensure that it only accepts valid port numbers (0-65535)
39 |
40 | Challenging:
41 |
42 | * The echo server as outlined will only process a connection from one client
43 | at a time. If a second client were to attempt a connection, it would have to
44 | wait until the first message was fully echoed before it could be dealt with.
45 |
46 | Python provides a module called `select` that allows waiting for I/O events
47 | in order to control flow. The `select.select` method can be used to allow
48 | our echo server to handle more than one incoming connection in "parallel".
49 |
50 | Read the documentation about the `select` module
51 | (http://docs.python.org/3/library/select.html) and attempt to write a second
52 | version of the echo server that can handle multiple client connections in
53 | "parallel". You do not need to invoke threading of any kind to do this.
54 |
--------------------------------------------------------------------------------
/resources/session01/tests.py:
--------------------------------------------------------------------------------
1 | from echo_client import client
2 | import socket
3 | import unittest
4 |
5 |
6 | class EchoTestCase(unittest.TestCase):
7 | """tests for the echo server and client"""
8 |
9 | def send_message(self, message):
10 | """Attempt to send a message using the client
11 |
12 | In case of a socket error, fail and report the problem
13 | """
14 | try:
15 | reply = client(message)
16 | except socket.error as e:
17 | if e.errno == 61:
18 | msg = "Error: {0}, is the server running?"
19 | self.fail(msg.format(e.strerror))
20 | else:
21 | self.fail("Unexpected Error: {0}".format(str(e)))
22 | return reply
23 |
24 | def test_short_message_echo(self):
25 | """test that a message short than 16 bytes echoes cleanly"""
26 | expected = "short message"
27 | actual = self.send_message(expected)
28 | self.assertEqual(
29 | expected,
30 | actual,
31 | "expected {0}, got {1}".format(expected, actual)
32 | )
33 |
34 | def test_long_message_echo(self):
35 | """test that a message longer than 16 bytes echoes in 16-byte chunks"""
36 | expected = "Four score and seven years ago our fathers did stuff"
37 | actual = self.send_message(expected)
38 | self.assertEqual(
39 | expected,
40 | actual,
41 | "expected {0}, got {1}".format(expected, actual)
42 | )
43 |
44 |
45 | if __name__ == '__main__':
46 | unittest.main()
47 |
--------------------------------------------------------------------------------
/resources/session02/homework/simple_client.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 |
4 |
5 | def bytes_client(msg):
6 | server_address = ('localhost', 10000)
7 | sock = socket.socket(
8 | socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP
9 | )
10 | print(
11 | 'connecting to {0} port {1}'.format(*server_address),
12 | file=sys.stderr
13 | )
14 | sock.connect(server_address)
15 | response = b''
16 | done = False
17 | bufsize = 1024
18 | try:
19 | print('sending "{0}"'.format(msg), file=sys.stderr)
20 | sock.sendall(msg.encode('utf8'))
21 | while not done:
22 | chunk = sock.recv(bufsize)
23 | if len(chunk) < bufsize:
24 | done = True
25 | response += chunk
26 | print('received "{0}"'.format(response), file=sys.stderr)
27 | finally:
28 | print('closing socket', file=sys.stderr)
29 | sock.close()
30 | return response
31 |
32 |
33 | def client(msg):
34 | return bytes_client(msg).decode('utf8')
35 |
36 |
37 | if __name__ == '__main__':
38 | if len(sys.argv) != 2:
39 | usg = '\nusage: python echo_client.py "this is my message"\n'
40 | print(usg, file=sys.stderr)
41 | sys.exit(1)
42 |
43 | msg = sys.argv[1]
44 | client(msg)
45 |
--------------------------------------------------------------------------------
/resources/session02/homework/webroot/a_web_page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | North Carolina
6 |
7 | A fine place to spend a week learning web programming!
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/resources/session02/homework/webroot/images/JPEG_example.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session02/homework/webroot/images/JPEG_example.jpg
--------------------------------------------------------------------------------
/resources/session02/homework/webroot/images/Sample_Scene_Balls.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session02/homework/webroot/images/Sample_Scene_Balls.jpg
--------------------------------------------------------------------------------
/resources/session02/homework/webroot/images/sample_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session02/homework/webroot/images/sample_1.png
--------------------------------------------------------------------------------
/resources/session02/homework/webroot/make_time.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | """
4 | make_time.py
5 |
6 | simple script that returns and HTML page with the current time
7 | """
8 |
9 | import datetime
10 |
11 | time_str = datetime.datetime.now().isoformat()
12 |
13 | html = """
14 |
15 |
16 | The time is:
17 | %s
18 |
19 |
20 | """ % time_str
21 |
22 | print(html)
23 |
--------------------------------------------------------------------------------
/resources/session02/homework/webroot/sample.txt:
--------------------------------------------------------------------------------
1 | This is a very simple text file.
2 | Just to show that we can server it up.
3 | It is three lines long.
4 |
--------------------------------------------------------------------------------
/resources/session02/http_server.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 |
4 |
5 | def server(log_buffer=sys.stderr):
6 | address = ('127.0.0.1', 10000)
7 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
8 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
9 | print("making a server on {0}:{1}".format(*address), file=log_buffer)
10 | sock.bind(address)
11 | sock.listen(1)
12 |
13 | try:
14 | while True:
15 | print('waiting for a connection', file=log_buffer)
16 | conn, addr = sock.accept() # blocking
17 | try:
18 | print('connection - {0}:{1}'.format(*addr), file=log_buffer)
19 | while True:
20 | data = conn.recv(16)
21 | print('received "{0}"'.format(data), file=log_buffer)
22 | if data:
23 | print('sending data back to client', file=log_buffer)
24 | conn.sendall(data)
25 | else:
26 | msg = 'no more data from {0}:{1}'.format(*addr)
27 | print(msg, log_buffer)
28 | break
29 | finally:
30 | conn.close()
31 |
32 | except KeyboardInterrupt:
33 | sock.close()
34 | return
35 |
36 |
37 | if __name__ == '__main__':
38 | server()
39 | sys.exit(0)
40 |
--------------------------------------------------------------------------------
/resources/session02/simple_client.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import sys
3 |
4 |
5 | def client(msg):
6 | server_address = ('localhost', 10000)
7 | sock = socket.socket(
8 | socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP
9 | )
10 | print(
11 | 'connecting to {0} port {1}'.format(*server_address),
12 | file=sys.stderr
13 | )
14 | sock.connect(server_address)
15 | response = ''
16 | done = False
17 | bufsize = 1024
18 | try:
19 | print('sending "{0}"'.format(msg), file=sys.stderr)
20 | sock.sendall(msg.encode('utf8'))
21 | while not done:
22 | chunk = sock.recv(bufsize)
23 | if len(chunk) < bufsize:
24 | done = True
25 | response += chunk.decode('utf8')
26 | print('received "{0}"'.format(response), file=sys.stderr)
27 | finally:
28 | print('closing socket', file=sys.stderr)
29 | sock.close()
30 | return response
31 |
32 |
33 | if __name__ == '__main__':
34 | if len(sys.argv) != 2:
35 | usg = '\nusage: python echo_client.py "this is my message"\n'
36 | print(usg, file=sys.stderr)
37 | sys.exit(1)
38 |
39 | msg = sys.argv[1]
40 | client(msg)
41 |
--------------------------------------------------------------------------------
/resources/session03/cgi/cgi-bin/cgi_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import cgi
3 |
4 |
5 | cgi.test()
6 |
--------------------------------------------------------------------------------
/resources/session03/cgi/cgi-bin/cgi_2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import cgi
3 | import cgitb
4 | cgitb.enable()
5 | import os
6 | import datetime
7 |
8 |
9 | default = "No Value Present"
10 |
11 |
12 | print("Content-Type: text/html")
13 | print()
14 |
15 | body = """
16 |
17 | Lab 1 - CGI experiments
18 |
19 |
20 | Hey there, this page has been generated by {software}, running {script}
21 | Today is {month} {date}, {year}.
22 | This page was requested by IP Address {client_ip}
23 |
24 | """.format(
25 | software=os.environ.get('SERVER_SOFTWARE', default),
26 | script='aaaa',
27 | month='bbbb',
28 | date='cccc',
29 | year='dddd',
30 | client_ip='eeee'
31 | )
32 | print(body)
33 |
--------------------------------------------------------------------------------
/resources/session03/cgi/cgi-bin/cgi_sums.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import cgi
3 | import cgitb
4 |
5 | cgitb.enable()
6 |
7 | print("Content-type: text/plain")
8 | print()
9 | print("Your job is to make this work")
10 |
--------------------------------------------------------------------------------
/resources/session03/cgi/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Python 200: Session 06 Lab Examples
5 |
6 |
7 | Python 200
8 | Session 06: CGI, WSGI and Living Online
9 | CGI Examples
10 |
11 | - CGI Test 1
12 | - Exercise One
13 | - CGI Sum Server
14 |
15 | WSGI Examples
16 |
17 |
18 |
--------------------------------------------------------------------------------
/resources/session03/wsgi/bookapp.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | from bookdb import BookDB
4 |
5 | DB = BookDB()
6 |
7 |
8 | def book(book_id):
9 | return "a book with id %s
" % book_id
10 |
11 |
12 | def books():
13 | return "a list of books
"
14 |
15 |
16 | def application(environ, start_response):
17 | status = "200 OK"
18 | headers = [('Content-type', 'text/html')]
19 | start_response(status, headers)
20 | return ["No Progress Yet
".encode('utf8')]
21 |
22 |
23 | if __name__ == '__main__':
24 | from wsgiref.simple_server import make_server
25 | srv = make_server('localhost', 8080, application)
26 | srv.serve_forever()
27 |
--------------------------------------------------------------------------------
/resources/session03/wsgi/bookdb.py:
--------------------------------------------------------------------------------
1 |
2 | class BookDB():
3 | def titles(self):
4 | titles = [
5 | dict(id=id, title=database[id]['title']) for id in database.keys()
6 | ]
7 | return titles
8 |
9 | def title_info(self, id):
10 | return database.get(id, None)
11 |
12 |
13 | # let's pretend we're getting this information from a database somewhere
14 | database = {
15 | 'id1': {
16 | 'title': 'CherryPy Essentials: Rapid Python Web Application Development',
17 | 'isbn': '978-1904811848',
18 | 'publisher': 'Packt Publishing (March 31, 2007)',
19 | 'author': 'Sylvain Hellegouarch',
20 | },
21 | 'id2': {
22 | 'title': 'Python for Software Design: How to Think Like a Computer Scientist',
23 | 'isbn': '978-0521725965',
24 | 'publisher': 'Cambridge University Press; 1 edition (March 16, 2009)',
25 | 'author': 'Allen B. Downey',
26 | },
27 | 'id3': {
28 | 'title': 'Foundations of Python Network Programming',
29 | 'isbn': '978-1430230038',
30 | 'publisher': 'Apress; 2 edition (December 21, 2010)',
31 | 'author': 'John Goerzen',
32 | },
33 | 'id4': {
34 | 'title': 'Python Cookbook, Second Edition',
35 | 'isbn': '978-0-596-00797-3',
36 | 'publisher': 'O''Reilly Media',
37 | 'author': 'Alex Martelli, Anna Ravenscroft, David Ascher',
38 | },
39 | 'id5': {
40 | 'title': 'The Pragmatic Programmer: From Journeyman to Master',
41 | 'isbn': '978-0201616224',
42 | 'publisher': 'Addison-Wesley Professional (October 30, 1999)',
43 | 'author': 'Andrew Hunt, David Thomas',
44 | },
45 | }
46 |
--------------------------------------------------------------------------------
/resources/session03/wsgi/wsgi_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import datetime
3 |
4 | default = "No Value Set"
5 |
6 | body = """
7 |
8 | Lab 3 - WSGI experiments
9 |
10 |
11 | Hey there, this page has been generated by {software}, running at {path}
12 | Today is {month} {date}, {year}.
13 | This page was requested by IP Address {client_ip}
14 |
15 | """
16 |
17 |
18 | def application(environ, start_response):
19 | import pprint
20 | pprint.pprint(environ)
21 |
22 | response_body = body.format(
23 | software=environ.get('SERVER_SOFTWARE', default),
24 | path="aaaa",
25 | month="bbbb",
26 | date="cccc",
27 | year="dddd",
28 | client_ip="eeee"
29 | )
30 | status = '200 OK'
31 |
32 | response_headers = [('Content-Type', 'text/html'),
33 | ('Content-Length', str(len(response_body)))]
34 | start_response(status, response_headers)
35 |
36 | return [response_body.encode('utf8')]
37 |
38 |
39 | if __name__ == '__main__':
40 | from wsgiref.simple_server import make_server
41 | srv = make_server('localhost', 8080, application)
42 | srv.serve_forever()
43 |
--------------------------------------------------------------------------------
/resources/session04/mashup_1.py:
--------------------------------------------------------------------------------
1 | from bs4 import BeautifulSoup
2 | import requests
3 |
4 |
5 | INSPECTION_DOMAIN = 'http://info.kingcounty.gov'
6 | INSPECTION_PATH = '/health/ehs/foodsafety/inspections/Results.aspx'
7 | INSPECTION_PARAMS = {
8 | 'Output': 'W',
9 | 'Business_Name': '',
10 | 'Business_Address': '',
11 | 'Longitude': '',
12 | 'Latitude': '',
13 | 'City': '',
14 | 'Zip_Code': '',
15 | 'Inspection_Type': 'All',
16 | 'Inspection_Start': '',
17 | 'Inspection_End': '',
18 | 'Inspection_Closed_Business': 'A',
19 | 'Violation_Points': '',
20 | 'Violation_Red_Points': '',
21 | 'Violation_Descr': '',
22 | 'Fuzzy_Search': 'N',
23 | 'Sort': 'H'
24 | }
25 |
26 |
27 | def get_inspection_page(**kwargs):
28 | url = INSPECTION_DOMAIN + INSPECTION_PATH
29 | params = INSPECTION_PARAMS.copy()
30 | for key, val in kwargs.items():
31 | if key in INSPECTION_PARAMS:
32 | params[key] = val
33 | resp = requests.get(url, params=params)
34 | resp.raise_for_status()
35 | return resp.text
36 |
37 |
38 | def parse_source(html):
39 | parsed = BeautifulSoup(html)
40 | return parsed
41 |
42 |
43 | if __name__ == '__main__':
44 | use_params = {
45 | 'Inspection_Start': '2/1/2014',
46 | 'Inspection_End': '2/1/2016',
47 | 'Zip_Code': '98101'
48 | }
49 | html = get_inspection_page(**use_params)
50 | parsed = parse_source(html)
51 | print(parsed.prettify())
52 |
--------------------------------------------------------------------------------
/resources/session04/mashup_2.py:
--------------------------------------------------------------------------------
1 | from bs4 import BeautifulSoup
2 | import pathlib
3 | import re
4 | import requests
5 |
6 |
7 | INSPECTION_DOMAIN = 'http://info.kingcounty.gov'
8 | INSPECTION_PATH = '/health/ehs/foodsafety/inspections/Results.aspx'
9 | INSPECTION_PARAMS = {
10 | 'Output': 'W',
11 | 'Business_Name': '',
12 | 'Business_Address': '',
13 | 'Longitude': '',
14 | 'Latitude': '',
15 | 'City': '',
16 | 'Zip_Code': '',
17 | 'Inspection_Type': 'All',
18 | 'Inspection_Start': '',
19 | 'Inspection_End': '',
20 | 'Inspection_Closed_Business': 'A',
21 | 'Violation_Points': '',
22 | 'Violation_Red_Points': '',
23 | 'Violation_Descr': '',
24 | 'Fuzzy_Search': 'N',
25 | 'Sort': 'H'
26 | }
27 |
28 |
29 | def get_inspection_page(**kwargs):
30 | url = INSPECTION_DOMAIN + INSPECTION_PATH
31 | params = INSPECTION_PARAMS.copy()
32 | for key, val in kwargs.items():
33 | if key in INSPECTION_PARAMS:
34 | params[key] = val
35 | resp = requests.get(url, params=params)
36 | resp.raise_for_status()
37 | return resp.text
38 |
39 |
40 | def parse_source(html):
41 | parsed = BeautifulSoup(html)
42 | return parsed
43 |
44 |
45 | def load_inspection_page(name):
46 | file_path = pathlib.Path(name)
47 | return file_path.read_text(encoding='utf8')
48 |
49 |
50 | def restaurant_data_generator(html):
51 | id_finder = re.compile(r'PR[\d]+~')
52 | return html.find_all('div', id=id_finder)
53 |
54 |
55 | if __name__ == '__main__':
56 | use_params = {
57 | 'Inspection_Start': '2/1/2013',
58 | 'Inspection_End': '2/1/2015',
59 | 'Zip_Code': '98101'
60 | }
61 | # html = get_inspection_page(**use_params)
62 | html = load_inspection_page('inspection_page.html')
63 | parsed = parse_source(html)
64 | content_col = parsed.find("td", id="contentcol")
65 | data_list = restaurant_data_generator(content_col)
66 | print(data_list[0].prettify())
67 |
--------------------------------------------------------------------------------
/resources/session04/mashup_3.py:
--------------------------------------------------------------------------------
1 | from bs4 import BeautifulSoup
2 | import pathlib
3 | import re
4 | import requests
5 |
6 |
7 | INSPECTION_DOMAIN = 'http://info.kingcounty.gov'
8 | INSPECTION_PATH = '/health/ehs/foodsafety/inspections/Results.aspx'
9 | INSPECTION_PARAMS = {
10 | 'Output': 'W',
11 | 'Business_Name': '',
12 | 'Business_Address': '',
13 | 'Longitude': '',
14 | 'Latitude': '',
15 | 'City': '',
16 | 'Zip_Code': '',
17 | 'Inspection_Type': 'All',
18 | 'Inspection_Start': '',
19 | 'Inspection_End': '',
20 | 'Inspection_Closed_Business': 'A',
21 | 'Violation_Points': '',
22 | 'Violation_Red_Points': '',
23 | 'Violation_Descr': '',
24 | 'Fuzzy_Search': 'N',
25 | 'Sort': 'H'
26 | }
27 |
28 |
29 | def get_inspection_page(**kwargs):
30 | url = INSPECTION_DOMAIN + INSPECTION_PATH
31 | params = INSPECTION_PARAMS.copy()
32 | for key, val in kwargs.items():
33 | if key in INSPECTION_PARAMS:
34 | params[key] = val
35 | resp = requests.get(url, params=params)
36 | resp.raise_for_status()
37 | return resp.text
38 |
39 |
40 | def parse_source(html):
41 | parsed = BeautifulSoup(html)
42 | return parsed
43 |
44 |
45 | def load_inspection_page(name):
46 | file_path = pathlib.Path(name)
47 | return file_path.read_text(encoding='utf8')
48 |
49 |
50 | def restaurant_data_generator(html):
51 | id_finder = re.compile(r'PR[\d]+~')
52 | return html.find_all('div', id=id_finder)
53 |
54 |
55 | def has_two_tds(elem):
56 | is_tr = elem.name == 'tr'
57 | td_children = elem.find_all('td', recursive=False)
58 | has_two = len(td_children) == 2
59 | return is_tr and has_two
60 |
61 |
62 | def clean_data(td):
63 | return td.text.strip(" \n:-")
64 |
65 |
66 | def extract_restaurant_metadata(elem):
67 | restaurant_data_rows = elem.find('tbody').find_all(
68 | has_two_tds, recursive=False
69 | )
70 | rdata = {}
71 | current_label = ''
72 | for data_row in restaurant_data_rows:
73 | key_cell, val_cell = data_row.find_all('td', recursive=False)
74 | new_label = clean_data(key_cell)
75 | current_label = new_label if new_label else current_label
76 | rdata.setdefault(current_label, []).append(clean_data(val_cell))
77 | return rdata
78 |
79 |
80 | if __name__ == '__main__':
81 | use_params = {
82 | 'Inspection_Start': '2/1/2013',
83 | 'Inspection_End': '2/1/2015',
84 | 'Zip_Code': '98101'
85 | }
86 | # html = get_inspection_page(**use_params)
87 | html = load_inspection_page('inspection_page.html')
88 | parsed = parse_source(html)
89 | content_col = parsed.find("td", id="contentcol")
90 | data_list = restaurant_data_generator(content_col)
91 | for data_div in data_list:
92 | metadata = extract_restaurant_metadata(data_div)
93 | print(metadata)
94 |
--------------------------------------------------------------------------------
/resources/session06/__init__.py:
--------------------------------------------------------------------------------
1 | from pyramid.config import Configurator
2 | from sqlalchemy import engine_from_config
3 |
4 | from .models import (
5 | DBSession,
6 | Base,
7 | )
8 |
9 |
10 | def create_session(settings):
11 | from sqlalchemy.orm import sessionmaker
12 | engine = engine_from_config(settings, 'sqlalchemy.')
13 | Session = sessionmaker(bind=engine)
14 | return Session()
15 |
16 |
17 | def main(global_config, **settings):
18 | """ This function returns a Pyramid WSGI application.
19 | """
20 | engine = engine_from_config(settings, 'sqlalchemy.')
21 | DBSession.configure(bind=engine)
22 | Base.metadata.bind = engine
23 | config = Configurator(settings=settings)
24 | config.include('pyramid_jinja2')
25 | config.add_static_view('static', 'static', cache_max_age=3600)
26 | config.add_route('home', '/')
27 | config.add_route('detail', '/journal/{id:\d+}')
28 | config.add_route('action', '/journal/{action}')
29 | config.scan()
30 | return config.make_wsgi_app()
31 |
--------------------------------------------------------------------------------
/resources/session06/development.ini:
--------------------------------------------------------------------------------
1 | ###
2 | # app configuration
3 | # http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/environment.html
4 | ###
5 |
6 | [app:main]
7 | use = egg:learning_journal
8 |
9 | pyramid.reload_templates = true
10 | pyramid.debug_authorization = false
11 | pyramid.debug_notfound = false
12 | pyramid.debug_routematch = false
13 | pyramid.default_locale_name = en
14 | pyramid.includes =
15 | pyramid_debugtoolbar
16 | pyramid_tm
17 |
18 | sqlalchemy.url = sqlite:///%(here)s/learning_journal.sqlite
19 |
20 | # By default, the toolbar only appears for clients from IP addresses
21 | # '127.0.0.1' and '::1'.
22 | # debugtoolbar.hosts = 127.0.0.1 ::1
23 |
24 |
25 | [pshell]
26 | create_session = learning_journal.create_session
27 | Entry = learning_journal.models.Entry
28 |
29 | ###
30 | # wsgi server configuration
31 | ###
32 |
33 | [server:main]
34 | use = egg:waitress#main
35 | host = 0.0.0.0
36 | port = 6543
37 |
38 | ###
39 | # logging configuration
40 | # http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/logging.html
41 | ###
42 |
43 | [loggers]
44 | keys = root, learning_journal, sqlalchemy
45 |
46 | [handlers]
47 | keys = console
48 |
49 | [formatters]
50 | keys = generic
51 |
52 | [logger_root]
53 | level = INFO
54 | handlers = console
55 |
56 | [logger_learning_journal]
57 | level = DEBUG
58 | handlers =
59 | qualname = learning_journal
60 |
61 | [logger_sqlalchemy]
62 | level = INFO
63 | handlers =
64 | qualname = sqlalchemy.engine
65 | # "level = INFO" logs SQL queries.
66 | # "level = DEBUG" logs SQL queries and results.
67 | # "level = WARN" logs neither. (Recommended for production systems.)
68 |
69 | [handler_console]
70 | class = StreamHandler
71 | args = (sys.stderr,)
72 | level = NOTSET
73 | formatter = generic
74 |
75 | [formatter_generic]
76 | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
77 |
--------------------------------------------------------------------------------
/resources/session06/forms.py:
--------------------------------------------------------------------------------
1 | from wtforms import (
2 | Form,
3 | TextField,
4 | TextAreaField,
5 | validators,
6 | )
7 |
8 | strip_filter = lambda x: x.strip() if x else None
9 |
10 |
11 | class EntryCreateForm(Form):
12 | title = TextField(
13 | 'Entry title',
14 | [validators.Length(min=1, max=255)],
15 | filters=[strip_filter]
16 | )
17 | body = TextAreaField(
18 | 'Entry body',
19 | [validators.Length(min=1)],
20 | filters=[strip_filter]
21 | )
22 |
--------------------------------------------------------------------------------
/resources/session06/layout.jinja2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Python Learning Journal
6 |
9 |
10 |
11 |
12 |
19 |
20 | My Python Journal
21 |
22 | {% block body %}{% endblock %}
23 |
24 |
25 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | .DS_Store
3 | *.egg-info
4 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/CHANGES.txt:
--------------------------------------------------------------------------------
1 | 0.0
2 | ---
3 |
4 | - Initial version
5 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.txt *.ini *.cfg *.rst
2 | recursive-include learning_journal *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
3 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/README.txt:
--------------------------------------------------------------------------------
1 | learning_journal README
2 | ==================
3 |
4 | Getting Started
5 | ---------------
6 |
7 | - cd
8 |
9 | - $VENV/bin/python setup.py develop
10 |
11 | - $VENV/bin/initialize_learning_journal_db development.ini
12 |
13 | - $VENV/bin/pserve development.ini
14 |
15 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/development.ini:
--------------------------------------------------------------------------------
1 | ###
2 | # app configuration
3 | # http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/environment.html
4 | ###
5 |
6 | [app:main]
7 | use = egg:learning_journal
8 |
9 | pyramid.reload_templates = true
10 | pyramid.debug_authorization = false
11 | pyramid.debug_notfound = false
12 | pyramid.debug_routematch = false
13 | pyramid.default_locale_name = en
14 | pyramid.includes =
15 | pyramid_debugtoolbar
16 | pyramid_tm
17 |
18 | sqlalchemy.url = sqlite:///%(here)s/learning_journal.sqlite
19 |
20 | # By default, the toolbar only appears for clients from IP addresses
21 | # '127.0.0.1' and '::1'.
22 | # debugtoolbar.hosts = 127.0.0.1 ::1
23 |
24 |
25 | [pshell]
26 | create_session = learning_journal.create_session
27 | Entry = learning_journal.models.Entry
28 |
29 | ###
30 | # wsgi server configuration
31 | ###
32 |
33 | [server:main]
34 | use = egg:waitress#main
35 | host = 0.0.0.0
36 | port = 6543
37 |
38 | ###
39 | # logging configuration
40 | # http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/logging.html
41 | ###
42 |
43 | [loggers]
44 | keys = root, learning_journal, sqlalchemy
45 |
46 | [handlers]
47 | keys = console
48 |
49 | [formatters]
50 | keys = generic
51 |
52 | [logger_root]
53 | level = INFO
54 | handlers = console
55 |
56 | [logger_learning_journal]
57 | level = DEBUG
58 | handlers =
59 | qualname = learning_journal
60 |
61 | [logger_sqlalchemy]
62 | level = INFO
63 | handlers =
64 | qualname = sqlalchemy.engine
65 | # "level = INFO" logs SQL queries.
66 | # "level = DEBUG" logs SQL queries and results.
67 | # "level = WARN" logs neither. (Recommended for production systems.)
68 |
69 | [handler_console]
70 | class = StreamHandler
71 | args = (sys.stderr,)
72 | level = NOTSET
73 | formatter = generic
74 |
75 | [formatter_generic]
76 | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
77 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/__init__.py:
--------------------------------------------------------------------------------
1 | from pyramid.config import Configurator
2 | from sqlalchemy import engine_from_config
3 |
4 | from .models import (
5 | DBSession,
6 | Base,
7 | )
8 |
9 |
10 | def create_session(settings):
11 | from sqlalchemy.orm import sessionmaker
12 | engine = engine_from_config(settings, 'sqlalchemy.')
13 | Session = sessionmaker(bind=engine)
14 | return Session()
15 |
16 |
17 | def main(global_config, **settings):
18 | """ This function returns a Pyramid WSGI application.
19 | """
20 | engine = engine_from_config(settings, 'sqlalchemy.')
21 | DBSession.configure(bind=engine)
22 | Base.metadata.bind = engine
23 | config = Configurator(settings=settings)
24 | config.include('pyramid_jinja2')
25 | config.add_static_view('static', 'static', cache_max_age=3600)
26 | config.add_route('home', '/')
27 | config.add_route('detail', '/journal/{id:\d+}')
28 | config.add_route('action', '/journal/{action}')
29 | config.scan()
30 | return config.make_wsgi_app()
31 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/forms.py:
--------------------------------------------------------------------------------
1 | from wtforms import (
2 | Form,
3 | TextField,
4 | TextAreaField,
5 | validators,
6 | )
7 |
8 | strip_filter = lambda x: x.strip() if x else None
9 |
10 |
11 | class EntryCreateForm(Form):
12 | title = TextField(
13 | 'Entry title',
14 | [validators.Length(min=1, max=255)],
15 | filters=[strip_filter]
16 | )
17 | body = TextAreaField(
18 | 'Entry body',
19 | [validators.Length(min=1)],
20 | filters=[strip_filter]
21 | )
22 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/models.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from sqlalchemy import (
3 | Column,
4 | DateTime,
5 | Index,
6 | Integer,
7 | Text,
8 | Unicode,
9 | UnicodeText,
10 | )
11 |
12 | import sqlalchemy as sa
13 | from sqlalchemy.ext.declarative import declarative_base
14 |
15 | from sqlalchemy.orm import (
16 | scoped_session,
17 | sessionmaker,
18 | )
19 |
20 | from zope.sqlalchemy import ZopeTransactionExtension
21 |
22 | DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
23 | Base = declarative_base()
24 |
25 |
26 | class MyModel(Base):
27 | __tablename__ = 'models'
28 | id = Column(Integer, primary_key=True)
29 | name = Column(Text)
30 | value = Column(Integer)
31 |
32 | Index('my_index', MyModel.name, unique=True, mysql_length=255)
33 |
34 |
35 | class Entry(Base):
36 | __tablename__ = 'entries'
37 | id = Column(Integer, primary_key=True)
38 | title = Column(Unicode(255), unique=True, nullable=False)
39 | body = Column(UnicodeText, default=u'')
40 | created = Column(DateTime, default=datetime.datetime.utcnow)
41 | edited = Column(DateTime, default=datetime.datetime.utcnow)
42 |
43 | @classmethod
44 | def all(cls, session=None):
45 | """return a query with all entries, ordered by creation date reversed
46 | """
47 | if session is None:
48 | session = DBSession
49 | return session.query(cls).order_by(sa.desc(cls.created)).all()
50 |
51 | @classmethod
52 | def by_id(cls, id, session=None):
53 | """return a single entry identified by id
54 |
55 | If no entry exists with the provided id, return None
56 | """
57 | if session is None:
58 | session = DBSession
59 | return session.query(cls).get(id)
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/scripts/__init__.py:
--------------------------------------------------------------------------------
1 | # package
2 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/scripts/initializedb.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import transaction
4 |
5 | from sqlalchemy import engine_from_config
6 |
7 | from pyramid.paster import (
8 | get_appsettings,
9 | setup_logging,
10 | )
11 |
12 | from pyramid.scripts.common import parse_vars
13 |
14 | from ..models import (
15 | DBSession,
16 | MyModel,
17 | Base,
18 | )
19 |
20 |
21 | def usage(argv):
22 | cmd = os.path.basename(argv[0])
23 | print('usage: %s [var=value]\n'
24 | '(example: "%s development.ini")' % (cmd, cmd))
25 | sys.exit(1)
26 |
27 |
28 | def main(argv=sys.argv):
29 | if len(argv) < 2:
30 | usage(argv)
31 | config_uri = argv[1]
32 | options = parse_vars(argv[2:])
33 | setup_logging(config_uri)
34 | settings = get_appsettings(config_uri, options=options)
35 | engine = engine_from_config(settings, 'sqlalchemy.')
36 | DBSession.configure(bind=engine)
37 | Base.metadata.create_all(engine)
38 | with transaction.manager:
39 | model = MyModel(name='one', value=1)
40 | DBSession.add(model)
41 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/static/pyramid-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session06/learning_journal/learning_journal/static/pyramid-16x16.png
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/static/pyramid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session06/learning_journal/learning_journal/static/pyramid.png
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/static/styles.css:
--------------------------------------------------------------------------------
1 | body{
2 | color:#111;
3 | padding:0;
4 | margin:0;
5 | background-color: #eee;}
6 | header{
7 | margin:0;
8 | padding:0 0.75em;
9 | width:100%;
10 | background: #222;
11 | color: #ccc;
12 | border-bottom: 3px solid #fff;}
13 | header:after{
14 | content:"";
15 | display:table;
16 | clear:both;}
17 | header a,
18 | footer a{
19 | text-decoration:none}
20 | header a:hover,
21 | footer a:hover {
22 | color:#fff;
23 | }
24 | header a:visited,
25 | footer a:visited {
26 | color:#eee;
27 | }
28 | header aside{
29 | float:right;
30 | text-align:right;
31 | padding-right:0.75em}
32 | header ul{
33 | list-style:none;
34 | list-style-type:none;
35 | display:inline-block}
36 | header ul li{
37 | margin:0 0.25em 0 0}
38 | header ul li a{
39 | padding:0;
40 | display:inline-block}
41 | main{padding:0 0.75em 1em}
42 | main:after{
43 | content:"";
44 | display:table;
45 | clear:both}
46 | main article{
47 | margin-bottom:1em;
48 | padding-left:0.5em}
49 | main article h3{margin-top:0}
50 | main article .entry_body{
51 | margin:0.5em}
52 | main aside{float:right}
53 | main aside .field{
54 | margin-bottom:1em}
55 | main aside .field input,
56 | main aside .field label,
57 | main aside .field textarea{
58 | vertical-align:top}
59 | main aside .field label{
60 | display:inline-block;
61 | width:15%;
62 | padding-top:2px}
63 | main aside .field input,
64 | main aside .field textarea{
65 | width:83%}
66 | main aside .control_row input{
67 | margin-left:16%}
68 | footer{
69 | padding: 1em 0.75em;
70 | background: #222;
71 | color: #ccc;
72 | border-top: 3px solid #fff;
73 | border-bottom: 3px solid #fff;}
74 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/static/theme.min.css:
--------------------------------------------------------------------------------
1 | @import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}}
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/templates/detail.jinja2:
--------------------------------------------------------------------------------
1 | {% extends "layout.jinja2" %}
2 | {% block body %}
3 |
4 | {{ entry.title }}
5 |
6 | {{ entry.body }}
7 |
8 | Created {{entry.created}}
9 |
10 | Go Back
11 | {% endblock %}
12 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/templates/edit.jinja2:
--------------------------------------------------------------------------------
1 | {% extends "templates/layout.jinja2" %}
2 | {% block body %}
3 | Create a Journal Entry
4 |
17 | {% endblock %}
18 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/templates/layout.jinja2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Python Learning Journal
6 |
9 |
10 |
11 |
12 |
19 |
20 | My Python Journal
21 |
22 | {% block body %}{% endblock %}
23 |
24 |
25 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/templates/list.jinja2:
--------------------------------------------------------------------------------
1 | {% extends "layout.jinja2" %}
2 | {% block body %}
3 | {% if entries %}
4 | Journal Entries
5 |
12 | {% else %}
13 | This journal is empty
14 | {% endif %}
15 | New Entry
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/tests.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import transaction
3 |
4 | from pyramid import testing
5 |
6 | from .models import DBSession
7 |
8 |
9 | class TestMyViewSuccessCondition(unittest.TestCase):
10 | def setUp(self):
11 | self.config = testing.setUp()
12 | from sqlalchemy import create_engine
13 | engine = create_engine('sqlite://')
14 | from .models import (
15 | Base,
16 | MyModel,
17 | )
18 | DBSession.configure(bind=engine)
19 | Base.metadata.create_all(engine)
20 | with transaction.manager:
21 | model = MyModel(name='one', value=55)
22 | DBSession.add(model)
23 |
24 | def tearDown(self):
25 | DBSession.remove()
26 | testing.tearDown()
27 |
28 | def test_passing_view(self):
29 | from .views import my_view
30 | request = testing.DummyRequest()
31 | info = my_view(request)
32 | self.assertEqual(info['one'].name, 'one')
33 | self.assertEqual(info['project'], 'learning_journal')
34 |
35 |
36 | class TestMyViewFailureCondition(unittest.TestCase):
37 | def setUp(self):
38 | self.config = testing.setUp()
39 | from sqlalchemy import create_engine
40 | engine = create_engine('sqlite://')
41 | from .models import (
42 | Base,
43 | MyModel,
44 | )
45 | DBSession.configure(bind=engine)
46 |
47 | def tearDown(self):
48 | DBSession.remove()
49 | testing.tearDown()
50 |
51 | def test_failing_view(self):
52 | from .views import my_view
53 | request = testing.DummyRequest()
54 | info = my_view(request)
55 | self.assertEqual(info.status_int, 500)
56 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/learning_journal/views.py:
--------------------------------------------------------------------------------
1 | from pyramid.httpexceptions import HTTPFound, HTTPNotFound
2 | from pyramid.view import view_config
3 |
4 | from .models import (
5 | DBSession,
6 | MyModel,
7 | Entry,
8 | )
9 |
10 | from .forms import EntryCreateForm
11 |
12 |
13 | @view_config(route_name='home', renderer='templates/list.jinja2')
14 | def index_page(request):
15 | entries = Entry.all()
16 | return {'entries': entries}
17 |
18 |
19 | @view_config(route_name='detail', renderer='templates/detail.jinja2')
20 | def view(request):
21 | this_id = request.matchdict.get('id', -1)
22 | entry = Entry.by_id(this_id)
23 | if not entry:
24 | return HTTPNotFound()
25 | return {'entry': entry}
26 |
27 |
28 | @view_config(route_name='action', match_param='action=create',
29 | renderer='templates/edit.jinja2')
30 | def create(request):
31 | entry = Entry()
32 | form = EntryCreateForm(request.POST)
33 | if request.method == 'POST' and form.validate():
34 | form.populate_obj(entry)
35 | DBSession.add(entry)
36 | return HTTPFound(location=request.route_url('home'))
37 | return {'form': form, 'action': request.matchdict.get('action')}
38 |
39 |
40 | @view_config(route_name='action', match_param='action=edit',
41 | renderer='string')
42 | def update(request):
43 | return 'edit page'
44 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/production.ini:
--------------------------------------------------------------------------------
1 | ###
2 | # app configuration
3 | # http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/environment.html
4 | ###
5 |
6 | [app:main]
7 | use = egg:learning_journal
8 |
9 | pyramid.reload_templates = false
10 | pyramid.debug_authorization = false
11 | pyramid.debug_notfound = false
12 | pyramid.debug_routematch = false
13 | pyramid.default_locale_name = en
14 | pyramid.includes =
15 | pyramid_tm
16 |
17 | sqlalchemy.url = sqlite:///%(here)s/learning_journal.sqlite
18 |
19 | [server:main]
20 | use = egg:waitress#main
21 | host = 0.0.0.0
22 | port = 6543
23 |
24 | ###
25 | # logging configuration
26 | # http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/logging.html
27 | ###
28 |
29 | [loggers]
30 | keys = root, learning_journal, sqlalchemy
31 |
32 | [handlers]
33 | keys = console
34 |
35 | [formatters]
36 | keys = generic
37 |
38 | [logger_root]
39 | level = WARN
40 | handlers = console
41 |
42 | [logger_learning_journal]
43 | level = WARN
44 | handlers =
45 | qualname = learning_journal
46 |
47 | [logger_sqlalchemy]
48 | level = WARN
49 | handlers =
50 | qualname = sqlalchemy.engine
51 | # "level = INFO" logs SQL queries.
52 | # "level = DEBUG" logs SQL queries and results.
53 | # "level = WARN" logs neither. (Recommended for production systems.)
54 |
55 | [handler_console]
56 | class = StreamHandler
57 | args = (sys.stderr,)
58 | level = NOTSET
59 | formatter = generic
60 |
61 | [formatter_generic]
62 | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
63 |
--------------------------------------------------------------------------------
/resources/session06/learning_journal/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from setuptools import setup, find_packages
4 |
5 | here = os.path.abspath(os.path.dirname(__file__))
6 | with open(os.path.join(here, 'README.txt')) as f:
7 | README = f.read()
8 | with open(os.path.join(here, 'CHANGES.txt')) as f:
9 | CHANGES = f.read()
10 |
11 | requires = [
12 | 'pyramid',
13 | 'pyramid_jinja2',
14 | 'pyramid_debugtoolbar',
15 | 'pyramid_tm',
16 | 'SQLAlchemy',
17 | 'transaction',
18 | 'zope.sqlalchemy',
19 | 'waitress',
20 | 'wtforms',
21 | ]
22 |
23 | setup(name='learning_journal',
24 | version='0.0',
25 | description='learning_journal',
26 | long_description=README + '\n\n' + CHANGES,
27 | classifiers=[
28 | "Programming Language :: Python",
29 | "Framework :: Pyramid",
30 | "Topic :: Internet :: WWW/HTTP",
31 | "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
32 | ],
33 | author='',
34 | author_email='',
35 | url='',
36 | keywords='web wsgi bfg pylons pyramid',
37 | packages=find_packages(),
38 | include_package_data=True,
39 | zip_safe=False,
40 | test_suite='learning_journal',
41 | install_requires=requires,
42 | entry_points="""\
43 | [paste.app_factory]
44 | main = learning_journal:main
45 | [console_scripts]
46 | initialize_learning_journal_db = learning_journal.scripts.initializedb:main
47 | """,
48 | )
49 |
--------------------------------------------------------------------------------
/resources/session06/models.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from sqlalchemy import (
3 | Column,
4 | DateTime,
5 | Index,
6 | Integer,
7 | Text,
8 | Unicode,
9 | UnicodeText,
10 | )
11 |
12 | import sqlalchemy as sa
13 | from sqlalchemy.ext.declarative import declarative_base
14 |
15 | from sqlalchemy.orm import (
16 | scoped_session,
17 | sessionmaker,
18 | )
19 |
20 | from zope.sqlalchemy import ZopeTransactionExtension
21 |
22 | DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
23 | Base = declarative_base()
24 |
25 |
26 | class MyModel(Base):
27 | __tablename__ = 'models'
28 | id = Column(Integer, primary_key=True)
29 | name = Column(Text)
30 | value = Column(Integer)
31 |
32 | Index('my_index', MyModel.name, unique=True, mysql_length=255)
33 |
34 |
35 | class Entry(Base):
36 | __tablename__ = 'entries'
37 | id = Column(Integer, primary_key=True)
38 | title = Column(Unicode(255), unique=True, nullable=False)
39 | body = Column(UnicodeText, default=u'')
40 | created = Column(DateTime, default=datetime.datetime.utcnow)
41 | edited = Column(DateTime, default=datetime.datetime.utcnow)
42 |
43 | @classmethod
44 | def all(cls, session=None):
45 | """return a query with all entries, ordered by creation date reversed
46 | """
47 | if session is None:
48 | session = DBSession
49 | return session.query(cls).order_by(sa.desc(cls.created)).all()
50 |
51 | @classmethod
52 | def by_id(cls, id, session=None):
53 | """return a single entry identified by id
54 |
55 | If no entry exists with the provided id, return None
56 | """
57 | if session is None:
58 | session = DBSession
59 | return session.query(cls).get(id)
60 |
--------------------------------------------------------------------------------
/resources/session06/styles.css:
--------------------------------------------------------------------------------
1 | body{
2 | color:#111;
3 | padding:0;
4 | margin:0;
5 | background-color: #eee;}
6 | header{
7 | margin:0;
8 | padding:0 0.75em;
9 | width:100%;
10 | background: #222;
11 | color: #ccc;
12 | border-bottom: 3px solid #fff;}
13 | header:after{
14 | content:"";
15 | display:table;
16 | clear:both;}
17 | header a,
18 | footer a{
19 | text-decoration:none}
20 | header a:hover,
21 | footer a:hover {
22 | color:#fff;
23 | }
24 | header a:visited,
25 | footer a:visited {
26 | color:#eee;
27 | }
28 | header aside{
29 | float:right;
30 | text-align:right;
31 | padding-right:0.75em}
32 | header ul{
33 | list-style:none;
34 | list-style-type:none;
35 | display:inline-block}
36 | header ul li{
37 | margin:0 0.25em 0 0}
38 | header ul li a{
39 | padding:0;
40 | display:inline-block}
41 | main{padding:0 0.75em 1em}
42 | main:after{
43 | content:"";
44 | display:table;
45 | clear:both}
46 | main article{
47 | margin-bottom:1em;
48 | padding-left:0.5em}
49 | main article h3{margin-top:0}
50 | main article .entry_body{
51 | margin:0.5em}
52 | main aside{float:right}
53 | main aside .field{
54 | margin-bottom:1em}
55 | main aside .field input,
56 | main aside .field label,
57 | main aside .field textarea{
58 | vertical-align:top}
59 | main aside .field label{
60 | display:inline-block;
61 | width:15%;
62 | padding-top:2px}
63 | main aside .field input,
64 | main aside .field textarea{
65 | width:83%}
66 | main aside .control_row input{
67 | margin-left:16%}
68 | footer{
69 | padding: 1em 0.75em;
70 | background: #222;
71 | color: #ccc;
72 | border-top: 3px solid #fff;
73 | border-bottom: 3px solid #fff;}
74 |
--------------------------------------------------------------------------------
/resources/session07/detail.jinja2:
--------------------------------------------------------------------------------
1 | {% extends "layout.jinja2" %}
2 | {% block body %}
3 |
4 | {{ entry.title }}
5 |
6 | {{ entry.body }}
7 |
8 | Created {{entry.created}}
9 |
10 |
11 | Go Back ::
12 |
13 | Edit Entry
14 |
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/resources/session07/forms.py:
--------------------------------------------------------------------------------
1 | from wtforms import (
2 | Form,
3 | HiddenField,
4 | TextField,
5 | TextAreaField,
6 | validators,
7 | )
8 |
9 | strip_filter = lambda x: x.strip() if x else None
10 |
11 |
12 | class EntryCreateForm(Form):
13 | title = TextField(
14 | 'Entry title',
15 | [validators.Length(min=1, max=255)],
16 | filters=[strip_filter]
17 | )
18 | body = TextAreaField(
19 | 'Entry body',
20 | [validators.Length(min=1)],
21 | filters=[strip_filter]
22 | )
23 |
24 |
25 | class EntryEditForm(EntryCreateForm):
26 | id = HiddenField()
27 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | .DS_Store
3 | *.egg-info
4 | *.sqlite
5 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/CHANGES.txt:
--------------------------------------------------------------------------------
1 | 0.0
2 | ---
3 |
4 | - Initial version
5 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.txt *.ini *.cfg *.rst
2 | recursive-include learning_journal *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
3 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/Procfile:
--------------------------------------------------------------------------------
1 | web: ./run
2 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/README.txt:
--------------------------------------------------------------------------------
1 | learning_journal README
2 | ==================
3 |
4 | Getting Started
5 | ---------------
6 |
7 | - cd
8 |
9 | - $VENV/bin/python setup.py develop
10 |
11 | - $VENV/bin/initialize_learning_journal_db development.ini
12 |
13 | - $VENV/bin/pserve development.ini
14 |
15 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/build_db:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | python setup.py develop
3 | initialize_learning_journal_db production.ini
4 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/development.ini:
--------------------------------------------------------------------------------
1 | ###
2 | # app configuration
3 | # http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/environment.html
4 | ###
5 |
6 | [app:main]
7 | use = egg:learning_journal
8 |
9 | pyramid.reload_templates = true
10 | pyramid.debug_authorization = false
11 | pyramid.debug_notfound = false
12 | pyramid.debug_routematch = false
13 | pyramid.default_locale_name = en
14 | pyramid.includes =
15 | pyramid_debugtoolbar
16 | pyramid_tm
17 |
18 | sqlalchemy.url = sqlite:///%(here)s/learning_journal.sqlite
19 |
20 | jinja2.filters =
21 | markdown = learning_journal.views.render_markdown
22 |
23 | # By default, the toolbar only appears for clients from IP addresses
24 | # '127.0.0.1' and '::1'.
25 | # debugtoolbar.hosts = 127.0.0.1 ::1
26 |
27 | [pshell]
28 | create_session = learning_journal.create_session
29 | Entry = learning_journal.models.Entry
30 |
31 | ###
32 | # wsgi server configuration
33 | ###
34 |
35 | [server:main]
36 | use = egg:waitress#main
37 | host = 0.0.0.0
38 | port = 6543
39 |
40 | ###
41 | # logging configuration
42 | # http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/logging.html
43 | ###
44 |
45 | [loggers]
46 | keys = root, learning_journal, sqlalchemy
47 |
48 | [handlers]
49 | keys = console
50 |
51 | [formatters]
52 | keys = generic
53 |
54 | [logger_root]
55 | level = INFO
56 | handlers = console
57 |
58 | [logger_learning_journal]
59 | level = DEBUG
60 | handlers =
61 | qualname = learning_journal
62 |
63 | [logger_sqlalchemy]
64 | level = INFO
65 | handlers =
66 | qualname = sqlalchemy.engine
67 | # "level = INFO" logs SQL queries.
68 | # "level = DEBUG" logs SQL queries and results.
69 | # "level = WARN" logs neither. (Recommended for production systems.)
70 |
71 | [handler_console]
72 | class = StreamHandler
73 | args = (sys.stderr,)
74 | level = NOTSET
75 | formatter = generic
76 |
77 | [formatter_generic]
78 | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
79 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | from pyramid.authentication import AuthTktAuthenticationPolicy
3 | from pyramid.authorization import ACLAuthorizationPolicy
4 | from pyramid.config import Configurator
5 | from sqlalchemy import engine_from_config
6 |
7 | from .models import (
8 | DBSession,
9 | Base,
10 | )
11 | from .security import EntryFactory
12 |
13 |
14 | def create_session(settings):
15 | from sqlalchemy.orm import sessionmaker
16 | engine = engine_from_config(settings, 'sqlalchemy.')
17 | Session = sessionmaker(bind=engine)
18 | return Session()
19 |
20 |
21 | def main(global_config, **settings):
22 | """ This function returns a Pyramid WSGI application.
23 | """
24 | if 'DATABASE_URL' in os.environ:
25 | settings['sqlalchemy.url'] = os.environ['DATABASE_URL']
26 | engine = engine_from_config(settings, 'sqlalchemy.')
27 | engine = engine_from_config(settings, 'sqlalchemy.')
28 | DBSession.configure(bind=engine)
29 | Base.metadata.bind = engine
30 | secret = os.environ.get('AUTH_SECRET', 'somesecret')
31 | config = Configurator(
32 | settings=settings,
33 | authentication_policy=AuthTktAuthenticationPolicy(secret),
34 | authorization_policy=ACLAuthorizationPolicy(),
35 | default_permission='view'
36 | )
37 | config.include('pyramid_jinja2')
38 | config.add_static_view('static', 'static', cache_max_age=3600)
39 | config.add_route('home', '/', factory=EntryFactory)
40 | config.add_route('detail', '/journal/{id:\d+}', factory=EntryFactory)
41 | config.add_route('action', '/journal/{action}', factory=EntryFactory)
42 | config.add_route('auth', '/sign/{action}', factory=EntryFactory)
43 | config.scan()
44 | return config.make_wsgi_app()
45 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/forms.py:
--------------------------------------------------------------------------------
1 | from wtforms import (
2 | Form,
3 | HiddenField,
4 | PasswordField,
5 | TextField,
6 | TextAreaField,
7 | validators,
8 | )
9 |
10 | strip_filter = lambda x: x.strip() if x else None
11 |
12 |
13 | class EntryCreateForm(Form):
14 | title = TextField(
15 | 'Entry title',
16 | [validators.Length(min=1, max=255)],
17 | filters=[strip_filter]
18 | )
19 | body = TextAreaField(
20 | 'Entry body',
21 | [validators.Length(min=1)],
22 | filters=[strip_filter]
23 | )
24 |
25 |
26 | class EntryEditForm(EntryCreateForm):
27 | id = HiddenField()
28 |
29 |
30 | class LoginForm(Form):
31 | username = TextField('Username', [validators.Length(min=1, max=255)])
32 | password = PasswordField('Password', [validators.Length(min=1, max=255)])
33 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/models.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from passlib.context import CryptContext
3 | from sqlalchemy import (
4 | Column,
5 | DateTime,
6 | Index,
7 | Integer,
8 | Text,
9 | Unicode,
10 | UnicodeText,
11 | )
12 |
13 | import sqlalchemy as sa
14 | from sqlalchemy.ext.declarative import declarative_base
15 |
16 | from sqlalchemy.orm import (
17 | scoped_session,
18 | sessionmaker,
19 | )
20 |
21 | from zope.sqlalchemy import ZopeTransactionExtension
22 |
23 | DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
24 | Base = declarative_base()
25 |
26 |
27 | password_context = CryptContext(schemes=['pbkdf2_sha512'])
28 |
29 |
30 | class MyModel(Base):
31 | __tablename__ = 'models'
32 | id = Column(Integer, primary_key=True)
33 | name = Column(Text)
34 | value = Column(Integer)
35 |
36 | Index('my_index', MyModel.name, unique=True, mysql_length=255)
37 |
38 |
39 | class Entry(Base):
40 | __tablename__ = 'entries'
41 | id = Column(Integer, primary_key=True)
42 | title = Column(Unicode(255), unique=True, nullable=False)
43 | body = Column(UnicodeText, default=u'')
44 | created = Column(DateTime, default=datetime.datetime.utcnow)
45 | edited = Column(DateTime, default=datetime.datetime.utcnow)
46 |
47 | @classmethod
48 | def all(cls, session=None):
49 | """return a query with all entries, ordered by creation date reversed
50 | """
51 | if session is None:
52 | session = DBSession
53 | return session.query(cls).order_by(sa.desc(cls.created)).all()
54 |
55 | @classmethod
56 | def by_id(cls, id, session=None):
57 | """return a single entry identified by id
58 |
59 | If no entry exists with the provided id, return None
60 | """
61 | if session is None:
62 | session = DBSession
63 | return session.query(cls).get(id)
64 |
65 |
66 | class User(Base):
67 | __tablename__ = 'users'
68 | id = Column(Integer, primary_key=True, autoincrement=True)
69 | name = Column(Unicode(255), unique=True, nullable=False)
70 | password = Column(Unicode(255), nullable=False)
71 |
72 | @classmethod
73 | def by_name(cls, name, session=None):
74 | if session is None:
75 | session = DBSession
76 | return DBSession.query(cls).filter(cls.name == name).first()
77 |
78 | def verify_password(self, password):
79 | return password_context.verify(password, self.password)
80 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/scripts/__init__.py:
--------------------------------------------------------------------------------
1 | # package
2 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/scripts/initializedb.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import transaction
4 |
5 | from sqlalchemy import engine_from_config
6 |
7 | from pyramid.paster import (
8 | get_appsettings,
9 | setup_logging,
10 | )
11 |
12 | from pyramid.scripts.common import parse_vars
13 |
14 | from ..models import (
15 | DBSession,
16 | MyModel,
17 | Base,
18 | User,
19 | password_context
20 | )
21 |
22 |
23 | def usage(argv):
24 | cmd = os.path.basename(argv[0])
25 | print('usage: %s [var=value]\n'
26 | '(example: "%s development.ini")' % (cmd, cmd))
27 | sys.exit(1)
28 |
29 |
30 | def main(argv=sys.argv):
31 | if len(argv) < 2:
32 | usage(argv)
33 | config_uri = argv[1]
34 | options = parse_vars(argv[2:])
35 | setup_logging(config_uri)
36 | settings = get_appsettings(config_uri, options=options)
37 | if 'DATABASE_URL' in os.environ:
38 | settings['sqlalchemy.url'] = os.environ['DATABASE_URL']
39 | engine = engine_from_config(settings, 'sqlalchemy.')
40 | DBSession.configure(bind=engine)
41 | Base.metadata.create_all(engine)
42 | with transaction.manager:
43 | password = os.environ.get('ADMIN_PASSWORD', 'admin')
44 | encrypted = password_context.encrypt(password)
45 | admin = User(name=u'admin', password=encrypted)
46 | DBSession.add(admin)
47 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/security.py:
--------------------------------------------------------------------------------
1 | from pyramid.security import Allow, Everyone, Authenticated
2 |
3 |
4 | class EntryFactory(object):
5 | __acl__ = [
6 | (Allow, Everyone, 'view'),
7 | (Allow, Authenticated, 'create'),
8 | (Allow, Authenticated, 'edit'),
9 | ]
10 |
11 | def __init__(self, request):
12 | pass
13 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/static/pyramid-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session07/learning_journal/learning_journal/static/pyramid-16x16.png
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/static/pyramid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session07/learning_journal/learning_journal/static/pyramid.png
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/static/theme.min.css:
--------------------------------------------------------------------------------
1 | @import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}}
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/templates/detail.jinja2:
--------------------------------------------------------------------------------
1 | {% extends "layout.jinja2" %}
2 | {% block body %}
3 |
4 | {{ entry.title }}
5 |
6 | {{ entry.body|markdown }}
7 |
8 | Created {{entry.created}}
9 |
10 |
11 | Go Back
12 | {% if logged_in %}
13 | ::
14 |
15 | Edit Entry
16 | {% endif %}
17 |
18 | {% endblock %}
19 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/templates/edit.jinja2:
--------------------------------------------------------------------------------
1 | {% extends "templates/layout.jinja2" %}
2 | {% block body %}
3 | Create a Journal Entry
4 |
17 | {% endblock %}
18 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/templates/layout.jinja2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Python Learning Journal
6 |
9 |
10 |
11 |
12 |
19 |
20 | My Python Journal
21 |
22 | {% block body %}{% endblock %}
23 |
24 |
25 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/templates/list.jinja2:
--------------------------------------------------------------------------------
1 | {% extends "layout.jinja2" %}
2 | {% block body %}
3 | {% if login_form %}
4 |
15 | {% endif %}
16 | {% if entries %}
17 | Journal Entries
18 |
19 | {% for entry in entries %}
20 | -
21 | {{ entry.title }}
22 |
23 | {% endfor %}
24 |
25 | {% else %}
26 | This journal is empty
27 | {% endif %}
28 | {% if not login_form %}
29 | New Entry
30 | {% endif %}
31 | {% endblock %}
32 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/learning_journal/tests.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import transaction
3 |
4 | from pyramid import testing
5 |
6 | from .models import DBSession
7 |
8 |
9 | class TestMyViewSuccessCondition(unittest.TestCase):
10 | def setUp(self):
11 | self.config = testing.setUp()
12 | from sqlalchemy import create_engine
13 | engine = create_engine('sqlite://')
14 | from .models import (
15 | Base,
16 | MyModel,
17 | )
18 | DBSession.configure(bind=engine)
19 | Base.metadata.create_all(engine)
20 | with transaction.manager:
21 | model = MyModel(name='one', value=55)
22 | DBSession.add(model)
23 |
24 | def tearDown(self):
25 | DBSession.remove()
26 | testing.tearDown()
27 |
28 | def test_passing_view(self):
29 | from .views import my_view
30 | request = testing.DummyRequest()
31 | info = my_view(request)
32 | self.assertEqual(info['one'].name, 'one')
33 | self.assertEqual(info['project'], 'learning_journal')
34 |
35 |
36 | class TestMyViewFailureCondition(unittest.TestCase):
37 | def setUp(self):
38 | self.config = testing.setUp()
39 | from sqlalchemy import create_engine
40 | engine = create_engine('sqlite://')
41 | from .models import (
42 | Base,
43 | MyModel,
44 | )
45 | DBSession.configure(bind=engine)
46 |
47 | def tearDown(self):
48 | DBSession.remove()
49 | testing.tearDown()
50 |
51 | def test_failing_view(self):
52 | from .views import my_view
53 | request = testing.DummyRequest()
54 | info = my_view(request)
55 | self.assertEqual(info.status_int, 500)
56 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/production.ini:
--------------------------------------------------------------------------------
1 | ###
2 | # app configuration
3 | # http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/environment.html
4 | ###
5 |
6 | [app:main]
7 | use = egg:learning_journal
8 |
9 |
10 | pyramid.reload_templates = false
11 | pyramid.debug_authorization = false
12 | pyramid.debug_notfound = false
13 | pyramid.debug_routematch = false
14 | pyramid.default_locale_name = en
15 | pyramid.includes =
16 | pyramid_tm
17 |
18 | sqlalchemy.url = sqlite:///%(here)s/learning_journal.sqlite
19 |
20 | jinja2.filters =
21 | markdown = learning_journal.views.render_markdown
22 |
23 | [server:main]
24 | use = egg:waitress#main
25 | host = 0.0.0.0
26 | port = 6543
27 |
28 | ###
29 | # logging configuration
30 | # http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/logging.html
31 | ###
32 |
33 | [loggers]
34 | keys = root, learning_journal, sqlalchemy
35 |
36 | [handlers]
37 | keys = console
38 |
39 | [formatters]
40 | keys = generic
41 |
42 | [logger_root]
43 | level = WARN
44 | handlers = console
45 |
46 | [logger_learning_journal]
47 | level = WARN
48 | handlers =
49 | qualname = learning_journal
50 |
51 | [logger_sqlalchemy]
52 | level = WARN
53 | handlers =
54 | qualname = sqlalchemy.engine
55 | # "level = INFO" logs SQL queries.
56 | # "level = DEBUG" logs SQL queries and results.
57 | # "level = WARN" logs neither. (Recommended for production systems.)
58 |
59 | [handler_console]
60 | class = StreamHandler
61 | args = (sys.stderr,)
62 | level = NOTSET
63 | formatter = generic
64 |
65 | [formatter_generic]
66 | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
67 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/requirements.txt:
--------------------------------------------------------------------------------
1 | appnope==0.1.0
2 | decorator==4.0.6
3 | ipython==4.0.1
4 | ipython-genutils==0.1.0
5 | Jinja2==2.8
6 | Mako==1.0.3
7 | Markdown==2.6.5
8 | MarkupSafe==0.23
9 | passlib==1.6.5
10 | PasteDeploy==1.5.2
11 | path.py==8.1.2
12 | pexpect==4.0.1
13 | pickleshare==0.5
14 | psycopg2==2.6.1
15 | ptyprocess==0.5
16 | Pygments==2.0.2
17 | pyramid==1.5.7
18 | pyramid-debugtoolbar==2.4.2
19 | pyramid-jinja2==2.5
20 | pyramid-mako==1.0.2
21 | pyramid-tm==0.12.1
22 | repoze.lru==0.6
23 | simplegeneric==0.8.1
24 | SQLAlchemy==1.0.11
25 | traitlets==4.0.0
26 | transaction==1.4.4
27 | translationstring==1.3
28 | venusian==1.0
29 | waitress==0.8.10
30 | WebOb==1.5.1
31 | WTForms==2.1
32 | zope.deprecation==4.1.2
33 | zope.interface==4.1.3
34 | zope.sqlalchemy==0.7.6
35 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/run:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | python setup.py develop
3 | python runapp.py
4 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/runapp.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from paste.deploy import loadapp
4 | from waitress import serve
5 |
6 | if __name__ == "__main__":
7 | port = int(os.environ.get("PORT", 5000))
8 | app = loadapp('config:production.ini', relative_to='.')
9 |
10 | serve(app, host='0.0.0.0', port=port)
11 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.5.0
2 |
--------------------------------------------------------------------------------
/resources/session07/learning_journal/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from setuptools import setup, find_packages
4 |
5 | here = os.path.abspath(os.path.dirname(__file__))
6 | with open(os.path.join(here, 'README.txt')) as f:
7 | README = f.read()
8 | with open(os.path.join(here, 'CHANGES.txt')) as f:
9 | CHANGES = f.read()
10 |
11 | requires = [
12 | 'pyramid',
13 | 'pyramid_jinja2',
14 | 'pyramid_debugtoolbar',
15 | 'pyramid_tm',
16 | 'SQLAlchemy',
17 | 'transaction',
18 | 'zope.sqlalchemy',
19 | 'waitress',
20 | 'wtforms',
21 | 'passlib',
22 | 'markdown',
23 | 'pygments',
24 | ]
25 |
26 | setup(name='learning_journal',
27 | version='0.0',
28 | description='learning_journal',
29 | long_description=README + '\n\n' + CHANGES,
30 | classifiers=[
31 | "Programming Language :: Python",
32 | "Framework :: Pyramid",
33 | "Topic :: Internet :: WWW/HTTP",
34 | "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
35 | ],
36 | author='',
37 | author_email='',
38 | url='',
39 | keywords='web wsgi bfg pylons pyramid',
40 | packages=find_packages(),
41 | include_package_data=True,
42 | zip_safe=False,
43 | test_suite='learning_journal',
44 | install_requires=requires,
45 | entry_points="""\
46 | [paste.app_factory]
47 | main = learning_journal:main
48 | [console_scripts]
49 | initialize_learning_journal_db = learning_journal.scripts.initializedb:main
50 | """,
51 | )
52 |
--------------------------------------------------------------------------------
/resources/session07/models.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from sqlalchemy import (
3 | Column,
4 | DateTime,
5 | Index,
6 | Integer,
7 | Text,
8 | Unicode,
9 | UnicodeText,
10 | )
11 |
12 | import sqlalchemy as sa
13 | from sqlalchemy.ext.declarative import declarative_base
14 |
15 | from sqlalchemy.orm import (
16 | scoped_session,
17 | sessionmaker,
18 | )
19 |
20 | from zope.sqlalchemy import ZopeTransactionExtension
21 |
22 | DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
23 | Base = declarative_base()
24 |
25 |
26 | class MyModel(Base):
27 | __tablename__ = 'models'
28 | id = Column(Integer, primary_key=True)
29 | name = Column(Text)
30 | value = Column(Integer)
31 |
32 | Index('my_index', MyModel.name, unique=True, mysql_length=255)
33 |
34 |
35 | class Entry(Base):
36 | __tablename__ = 'entries'
37 | id = Column(Integer, primary_key=True)
38 | title = Column(Unicode(255), unique=True, nullable=False)
39 | body = Column(UnicodeText, default=u'')
40 | created = Column(DateTime, default=datetime.datetime.utcnow)
41 | edited = Column(DateTime, default=datetime.datetime.utcnow)
42 |
43 | @classmethod
44 | def all(cls, session=None):
45 | """return a query with all entries, ordered by creation date reversed
46 | """
47 | if session is None:
48 | session = DBSession
49 | return session.query(cls).order_by(sa.desc(cls.created)).all()
50 |
51 | @classmethod
52 | def by_id(cls, id, session=None):
53 | """return a single entry identified by id
54 |
55 | If no entry exists with the provided id, return None
56 | """
57 | if session is None:
58 | session = DBSession
59 | return session.query(cls).get(id)
60 |
61 |
62 | class User(Base):
63 | __tablename__ = 'users'
64 | id = Column(Integer, primary_key=True, autoincrement=True)
65 | name = Column(Unicode(255), unique=True, nullable=False)
66 | password = Column(Unicode(255), nullable=False)
67 |
68 | @classmethod
69 | def by_name(cls, name, session=None):
70 | if session is None:
71 | session = DBSession
72 | return DBSession.query(cls).filter(cls.name == name).first()
73 |
--------------------------------------------------------------------------------
/resources/session07/views.py:
--------------------------------------------------------------------------------
1 | from pyramid.httpexceptions import HTTPFound, HTTPNotFound
2 | from pyramid.view import view_config
3 |
4 | from .models import (
5 | DBSession,
6 | MyModel,
7 | Entry,
8 | )
9 |
10 | from .forms import (
11 | EntryCreateForm,
12 | EntryEditForm,
13 | )
14 |
15 |
16 | @view_config(route_name='home', renderer='templates/list.jinja2')
17 | def index_page(request):
18 | entries = Entry.all()
19 | return {'entries': entries}
20 |
21 |
22 | @view_config(route_name='detail', renderer='templates/detail.jinja2')
23 | def view(request):
24 | this_id = request.matchdict.get('id', -1)
25 | entry = Entry.by_id(this_id)
26 | if not entry:
27 | return HTTPNotFound()
28 | return {'entry': entry}
29 |
30 |
31 | @view_config(route_name='action', match_param='action=create',
32 | renderer='templates/edit.jinja2')
33 | def create(request):
34 | entry = Entry()
35 | form = EntryCreateForm(request.POST)
36 | if request.method == 'POST' and form.validate():
37 | form.populate_obj(entry)
38 | DBSession.add(entry)
39 | return HTTPFound(location=request.route_url('home'))
40 | return {'form': form, 'action': request.matchdict.get('action')}
41 |
42 |
43 | @view_config(route_name='action', match_param='action=edit',
44 | renderer='templates/edit.jinja2')
45 | def update(request):
46 | id = int(request.params.get('id', -1))
47 | entry = Entry.by_id(id)
48 | if not entry:
49 | return HTTPNotFound()
50 | form = EntryEditForm(request.POST, entry)
51 | if request.method == 'POST' and form.validate():
52 | form.populate_obj(entry)
53 | return HTTPFound(location=request.route_url('detail', id=entry.id))
54 | return {'form': form, 'action': request.matchdict.get('action')}
55 |
--------------------------------------------------------------------------------
/resources/session08/django_blog.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #eee;
3 | color: #111;
4 | font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
5 | margin:0;
6 | padding:0;
7 | }
8 | #container {
9 | margin:0;
10 | padding:0;
11 | margin-top: 0px;
12 | }
13 | #header {
14 | background-color: #333;
15 | border-botton: 1px solid #111;
16 | margin:0;
17 | padding:0;
18 | }
19 | #control-bar {
20 | margin: 0em 0em 1em;
21 | list-style: none;
22 | list-style-type: none;
23 | text-align: right;
24 | color: #eee;
25 | font-size: 80%;
26 | padding-bottom: 0.4em;
27 | }
28 | #control-bar li {
29 | display: inline-block;
30 | }
31 | #control-bar li a {
32 | color: #eee;
33 | padding: 0.5em;
34 | text-decoration: none;
35 | }
36 | #control-bar li a:hover {
37 | color: #cce;
38 | }
39 | #content {
40 | margin: 0em 1em 1em;
41 | }
42 |
43 | ul#entries {
44 | list-style: none;
45 | list-style-type: none;
46 | }
47 | div.entry {
48 | margin-right: 2em;
49 | margin-top: 1em;
50 | border-top: 1px solid #cecece;
51 | }
52 | ul#entries li:first-child div.entry {
53 | border-top: none;
54 | margin-top: 0em;
55 | }
56 | div.entry-body {
57 | margin-left: 2em;
58 | }
59 | .notification {
60 | float: right;
61 | text-align: center;
62 | width: 25%;
63 | padding: 1em;
64 | }
65 | .info {
66 | background-color: #aae;
67 | }
68 | ul.categories {
69 | list-style: none;
70 | list-style-type: none;
71 | }
72 | ul.categories li {
73 | display: inline;
74 | }
75 |
--------------------------------------------------------------------------------
/resources/session08/myblog_test_fixture.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pk": 1,
4 | "model": "auth.user",
5 | "fields": {
6 | "username": "admin",
7 | "first_name": "Mr.",
8 | "last_name": "Administrator",
9 | "is_active": true,
10 | "is_superuser": true,
11 | "is_staff": true,
12 | "last_login": "2013-05-24T05:35:58.628Z",
13 | "groups": [],
14 | "user_permissions": [],
15 | "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=",
16 | "email": "admin@example.com",
17 | "date_joined": "2013-05-24T05:35:58.628Z"
18 | }
19 | },
20 | {
21 | "pk": 2,
22 | "model": "auth.user",
23 | "fields": {
24 | "username": "noname",
25 | "first_name": "",
26 | "last_name": "",
27 | "is_active": true,
28 | "is_superuser": true,
29 | "is_staff": true,
30 | "last_login": "2013-05-24T05:35:58.628Z",
31 | "groups": [],
32 | "user_permissions": [],
33 | "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=",
34 | "email": "noname@example.com",
35 | "date_joined": "2013-05-24T05:35:58.628Z"
36 | }
37 | }
38 | ]
39 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/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", "mysite.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/myblog/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session08/mysite_stage_1/myblog/__init__.py
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/myblog/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from myblog.models import Category
4 | from myblog.models import Post
5 |
6 |
7 | admin.site.register(Category)
8 | admin.site.register(Post)
9 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/myblog/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class MyblogConfig(AppConfig):
5 | name = 'myblog'
6 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/myblog/fixtures/myblog_test_fixture.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pk": 1,
4 | "model": "auth.user",
5 | "fields": {
6 | "username": "admin",
7 | "first_name": "Mr.",
8 | "last_name": "Administrator",
9 | "is_active": true,
10 | "is_superuser": true,
11 | "is_staff": true,
12 | "last_login": "2013-05-24T05:35:58.628Z",
13 | "groups": [],
14 | "user_permissions": [],
15 | "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=",
16 | "email": "admin@example.com",
17 | "date_joined": "2013-05-24T05:35:58.628Z"
18 | }
19 | },
20 | {
21 | "pk": 2,
22 | "model": "auth.user",
23 | "fields": {
24 | "username": "noname",
25 | "first_name": "",
26 | "last_name": "",
27 | "is_active": true,
28 | "is_superuser": true,
29 | "is_staff": true,
30 | "last_login": "2013-05-24T05:35:58.628Z",
31 | "groups": [],
32 | "user_permissions": [],
33 | "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=",
34 | "email": "noname@example.com",
35 | "date_joined": "2013-05-24T05:35:58.628Z"
36 | }
37 | }
38 | ]
39 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/myblog/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9 on 2015-12-31 19:13
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | initial = True
13 |
14 | dependencies = [
15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='Post',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('title', models.CharField(max_length=128)),
24 | ('text', models.TextField(blank=True)),
25 | ('created_date', models.DateTimeField(auto_now_add=True)),
26 | ('modified_date', models.DateTimeField(auto_now=True)),
27 | ('published_date', models.DateTimeField(blank=True, null=True)),
28 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
29 | ],
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/myblog/migrations/0002_category.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9 on 2015-12-31 21:40
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('myblog', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Category',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('name', models.CharField(max_length=128)),
20 | ('description', models.TextField(blank=True)),
21 | ('posts', models.ManyToManyField(blank=True, related_name='categories', to='myblog.Post')),
22 | ],
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/myblog/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session08/mysite_stage_1/myblog/migrations/__init__.py
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/myblog/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 |
4 |
5 | class Post(models.Model):
6 | title = models.CharField(max_length=128)
7 | text = models.TextField(blank=True)
8 | author = models.ForeignKey(User)
9 | created_date = models.DateTimeField(auto_now_add=True)
10 | modified_date = models.DateTimeField(auto_now=True)
11 | published_date = models.DateTimeField(blank=True, null=True)
12 |
13 | def __str__(self):
14 | return self.title
15 |
16 |
17 | class Category(models.Model):
18 | name = models.CharField(max_length=128)
19 | description = models.TextField(blank=True)
20 | posts = models.ManyToManyField(
21 | Post,
22 | blank=True,
23 | related_name='categories'
24 | )
25 |
26 | def __str__(self):
27 | return self.name
28 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/myblog/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.contrib.auth.models import User
3 |
4 | from myblog.models import Category
5 | from myblog.models import Post
6 |
7 |
8 | class PostTestCase(TestCase):
9 | fixtures = ['myblog_test_fixture.json']
10 |
11 | def setUp(self):
12 | self.user = User.objects.get(pk=1)
13 |
14 | def test_string_representation(self):
15 | expected = "This is a title"
16 | p1 = Post(title=expected)
17 | actual = str(p1)
18 | self.assertEqual(expected, actual)
19 |
20 |
21 | class CategoryTestCase(TestCase):
22 |
23 | def test_string_representation(self):
24 | expected = "A Category"
25 | c1 = Category(name=expected)
26 | actual = str(c1)
27 | self.assertEqual(expected, actual)
28 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/myblog/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | # Create your views here.
4 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/mysite/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session08/mysite_stage_1/mysite/__init__.py
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/mysite/urls.py:
--------------------------------------------------------------------------------
1 | """mysite URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.9/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: url(r'^$', 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: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Add an import: from blog import urls as blog_urls
14 | 2. Import the include() function: from django.conf.urls import url, include
15 | 3. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
16 | """
17 | from django.conf.urls import url
18 | from django.contrib import admin
19 |
20 | urlpatterns = [
21 | url(r'^admin/', admin.site.urls),
22 | ]
23 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_1/mysite/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for mysite 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/1.9/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", "mysite.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/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", "mysite.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session08/mysite_stage_2/myblog/__init__.py
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from myblog.models import Category
4 | from myblog.models import Post
5 |
6 |
7 | admin.site.register(Category)
8 | admin.site.register(Post)
9 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class MyblogConfig(AppConfig):
5 | name = 'myblog'
6 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/fixtures/myblog_test_fixture.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pk": 1,
4 | "model": "auth.user",
5 | "fields": {
6 | "username": "admin",
7 | "first_name": "Mr.",
8 | "last_name": "Administrator",
9 | "is_active": true,
10 | "is_superuser": true,
11 | "is_staff": true,
12 | "last_login": "2013-05-24T05:35:58.628Z",
13 | "groups": [],
14 | "user_permissions": [],
15 | "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=",
16 | "email": "admin@example.com",
17 | "date_joined": "2013-05-24T05:35:58.628Z"
18 | }
19 | },
20 | {
21 | "pk": 2,
22 | "model": "auth.user",
23 | "fields": {
24 | "username": "noname",
25 | "first_name": "",
26 | "last_name": "",
27 | "is_active": true,
28 | "is_superuser": true,
29 | "is_staff": true,
30 | "last_login": "2013-05-24T05:35:58.628Z",
31 | "groups": [],
32 | "user_permissions": [],
33 | "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=",
34 | "email": "noname@example.com",
35 | "date_joined": "2013-05-24T05:35:58.628Z"
36 | }
37 | }
38 | ]
39 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9 on 2015-12-31 19:13
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | initial = True
13 |
14 | dependencies = [
15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='Post',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('title', models.CharField(max_length=128)),
24 | ('text', models.TextField(blank=True)),
25 | ('created_date', models.DateTimeField(auto_now_add=True)),
26 | ('modified_date', models.DateTimeField(auto_now=True)),
27 | ('published_date', models.DateTimeField(blank=True, null=True)),
28 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
29 | ],
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/migrations/0002_category.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9 on 2015-12-31 21:40
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('myblog', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Category',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('name', models.CharField(max_length=128)),
20 | ('description', models.TextField(blank=True)),
21 | ('posts', models.ManyToManyField(blank=True, related_name='categories', to='myblog.Post')),
22 | ],
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session08/mysite_stage_2/myblog/migrations/__init__.py
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 |
4 |
5 | class Post(models.Model):
6 | title = models.CharField(max_length=128)
7 | text = models.TextField(blank=True)
8 | author = models.ForeignKey(User)
9 | created_date = models.DateTimeField(auto_now_add=True)
10 | modified_date = models.DateTimeField(auto_now=True)
11 | published_date = models.DateTimeField(blank=True, null=True)
12 |
13 | def __str__(self):
14 | return self.title
15 |
16 |
17 | class Category(models.Model):
18 | name = models.CharField(max_length=128)
19 | description = models.TextField(blank=True)
20 | posts = models.ManyToManyField(
21 | Post,
22 | blank=True,
23 | related_name='categories'
24 | )
25 |
26 | def __str__(self):
27 | return self.name
28 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/templates/list.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 | Recent Posts
5 |
6 | {% comment %} here is where the query happens {% endcomment %}
7 | {% for post in posts %}
8 |
9 |
{{ post }}
10 |
11 | Posted by {{ post.author_name }} — {{ post.published_date }}
12 |
13 |
14 | {{ post.text }}
15 |
16 |
17 | {% for category in post.categories.all %}
18 | - {{ category }}
19 | {% endfor %}
20 |
21 |
22 | {% endfor %}
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/tests.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from django.contrib.auth.models import User
3 | from django.test import TestCase
4 | from django.utils.timezone import utc
5 |
6 | from myblog.models import Category
7 | from myblog.models import Post
8 |
9 |
10 | class PostTestCase(TestCase):
11 | fixtures = ['myblog_test_fixture.json']
12 |
13 | def setUp(self):
14 | self.user = User.objects.get(pk=1)
15 |
16 | def test_string_representation(self):
17 | expected = "This is a title"
18 | p1 = Post(title=expected)
19 | actual = str(p1)
20 | self.assertEqual(expected, actual)
21 |
22 |
23 | class CategoryTestCase(TestCase):
24 |
25 | def test_string_representation(self):
26 | expected = "A Category"
27 | c1 = Category(name=expected)
28 | actual = str(c1)
29 | self.assertEqual(expected, actual)
30 |
31 |
32 | class FrontEndTestCase(TestCase):
33 | """test views provided in the front-end"""
34 | fixtures = ['myblog_test_fixture.json', ]
35 |
36 | def setUp(self):
37 | self.now = datetime.datetime.utcnow().replace(tzinfo=utc)
38 | self.timedelta = datetime.timedelta(15)
39 | author = User.objects.get(pk=1)
40 | for count in range(1, 11):
41 | post = Post(title="Post %d Title" % count,
42 | text="foo",
43 | author=author)
44 | if count < 6:
45 | # publish the first five posts
46 | pubdate = self.now - self.timedelta * count
47 | post.published_date = pubdate
48 | post.save()
49 |
50 | def test_list_only_published(self):
51 | resp = self.client.get('/')
52 | # the content of the rendered response is always a bytestring
53 | resp_text = resp.content.decode(resp.charset)
54 | self.assertTrue("Recent Posts" in resp_text)
55 | for count in range(1, 11):
56 | title = "Post %d Title" % count
57 | if count < 6:
58 | self.assertContains(resp, title, count=1)
59 | else:
60 | self.assertNotContains(resp, title)
61 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from myblog.views import stub_view
4 | from myblog.views import list_view
5 |
6 |
7 | urlpatterns = [
8 | url(r'^$',
9 | list_view,
10 | name="blog_index"),
11 | url(r'^posts/(?P\d+)/$',
12 | stub_view,
13 | name='blog_detail'),
14 | ]
15 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/myblog/views.py:
--------------------------------------------------------------------------------
1 | from django.http import HttpResponse, HttpResponseRedirect, Http404
2 | from django.shortcuts import render
3 | from django.template import RequestContext, loader
4 |
5 | from myblog.models import Post
6 |
7 |
8 | def stub_view(request, *args, **kwargs):
9 | body = "Stub View\n\n"
10 | if args:
11 | body += "Args:\n"
12 | body += "\n".join(["\t%s" % a for a in args])
13 | if kwargs:
14 | body += "Kwargs:\n"
15 | body += "\n".join(["\t%s: %s" % i for i in kwargs.items()])
16 | return HttpResponse(body, content_type="text/plain")
17 |
18 |
19 | def list_view(request):
20 | published = Post.objects.exclude(published_date__exact=None)
21 | posts = published.order_by('-published_date')
22 | context = {'posts': posts}
23 | return render(request, 'list.html', context)
24 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/mysite/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session08/mysite_stage_2/mysite/__init__.py
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/mysite/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | My Django Blog
5 |
6 |
7 |
8 |
9 | {% block content %}
10 | [content will go here]
11 | {% endblock %}
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/mysite/urls.py:
--------------------------------------------------------------------------------
1 | """mysite URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.9/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: url(r'^$', 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: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Add an import: from blog import urls as blog_urls
14 | 2. Import the include() function: from django.conf.urls import url, include
15 | 3. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
16 | """
17 | from django.conf.urls import url
18 | from django.conf.urls import include
19 | from django.contrib import admin
20 |
21 | urlpatterns = [
22 | url(r'^', include('myblog.urls')),
23 | url(r'^admin/', admin.site.urls),
24 | ]
25 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_2/mysite/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for mysite 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/1.9/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", "mysite.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/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", "mysite.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session08/mysite_stage_3/myblog/__init__.py
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from myblog.models import Category
4 | from myblog.models import Post
5 |
6 |
7 | admin.site.register(Category)
8 | admin.site.register(Post)
9 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class MyblogConfig(AppConfig):
5 | name = 'myblog'
6 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/fixtures/myblog_test_fixture.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pk": 1,
4 | "model": "auth.user",
5 | "fields": {
6 | "username": "admin",
7 | "first_name": "Mr.",
8 | "last_name": "Administrator",
9 | "is_active": true,
10 | "is_superuser": true,
11 | "is_staff": true,
12 | "last_login": "2013-05-24T05:35:58.628Z",
13 | "groups": [],
14 | "user_permissions": [],
15 | "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=",
16 | "email": "admin@example.com",
17 | "date_joined": "2013-05-24T05:35:58.628Z"
18 | }
19 | },
20 | {
21 | "pk": 2,
22 | "model": "auth.user",
23 | "fields": {
24 | "username": "noname",
25 | "first_name": "",
26 | "last_name": "",
27 | "is_active": true,
28 | "is_superuser": true,
29 | "is_staff": true,
30 | "last_login": "2013-05-24T05:35:58.628Z",
31 | "groups": [],
32 | "user_permissions": [],
33 | "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=",
34 | "email": "noname@example.com",
35 | "date_joined": "2013-05-24T05:35:58.628Z"
36 | }
37 | }
38 | ]
39 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9 on 2015-12-31 19:13
3 | from __future__ import unicode_literals
4 |
5 | from django.conf import settings
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | initial = True
13 |
14 | dependencies = [
15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='Post',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('title', models.CharField(max_length=128)),
24 | ('text', models.TextField(blank=True)),
25 | ('created_date', models.DateTimeField(auto_now_add=True)),
26 | ('modified_date', models.DateTimeField(auto_now=True)),
27 | ('published_date', models.DateTimeField(blank=True, null=True)),
28 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
29 | ],
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/migrations/0002_category.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9 on 2015-12-31 21:40
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('myblog', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Category',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('name', models.CharField(max_length=128)),
20 | ('description', models.TextField(blank=True)),
21 | ('posts', models.ManyToManyField(blank=True, related_name='categories', to='myblog.Post')),
22 | ],
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session08/mysite_stage_3/myblog/migrations/__init__.py
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 |
4 |
5 | class Post(models.Model):
6 | title = models.CharField(max_length=128)
7 | text = models.TextField(blank=True)
8 | author = models.ForeignKey(User)
9 | created_date = models.DateTimeField(auto_now_add=True)
10 | modified_date = models.DateTimeField(auto_now=True)
11 | published_date = models.DateTimeField(blank=True, null=True)
12 |
13 | def __str__(self):
14 | return self.title
15 |
16 |
17 | class Category(models.Model):
18 | name = models.CharField(max_length=128)
19 | description = models.TextField(blank=True)
20 | posts = models.ManyToManyField(
21 | Post,
22 | blank=True,
23 | related_name='categories'
24 | )
25 |
26 | def __str__(self):
27 | return self.name
28 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/static/django_blog.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #eee;
3 | color: #111;
4 | font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
5 | margin:0;
6 | padding:0;
7 | }
8 | #container {
9 | margin:0;
10 | padding:0;
11 | margin-top: 0px;
12 | }
13 | #header {
14 | background-color: #333;
15 | border-botton: 1px solid #111;
16 | margin:0;
17 | padding:0;
18 | }
19 | #control-bar {
20 | margin: 0em 0em 1em;
21 | list-style: none;
22 | list-style-type: none;
23 | text-align: right;
24 | color: #eee;
25 | font-size: 80%;
26 | padding-bottom: 0.4em;
27 | }
28 | #control-bar li {
29 | display: inline-block;
30 | }
31 | #control-bar li a {
32 | color: #eee;
33 | padding: 0.5em;
34 | text-decoration: none;
35 | }
36 | #control-bar li a:hover {
37 | color: #cce;
38 | }
39 | #content {
40 | margin: 0em 1em 1em;
41 | }
42 |
43 | ul#entries {
44 | list-style: none;
45 | list-style-type: none;
46 | }
47 | div.entry {
48 | margin-right: 2em;
49 | margin-top: 1em;
50 | border-top: 1px solid #cecece;
51 | }
52 | ul#entries li:first-child div.entry {
53 | border-top: none;
54 | margin-top: 0em;
55 | }
56 | div.entry-body {
57 | margin-left: 2em;
58 | }
59 | .notification {
60 | float: right;
61 | text-align: center;
62 | width: 25%;
63 | padding: 1em;
64 | }
65 | .info {
66 | background-color: #aae;
67 | }
68 | ul.categories {
69 | list-style: none;
70 | list-style-type: none;
71 | }
72 | ul.categories li {
73 | display: inline;
74 | }
75 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/templates/detail.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 | Home
5 | {{ post }}
6 |
7 | Posted by {{ post.author_name }} — {{ post.published_date }}
8 |
9 |
10 | {{ post.text }}
11 |
12 |
13 | {% for category in post.categories.all %}
14 | - {{ category }}
15 | {% endfor %}
16 |
17 | {% endblock %}
18 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/templates/list.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 | Recent Posts
5 |
6 | {% comment %} here is where the query happens {% endcomment %}
7 | {% for post in posts %}
8 |
9 |
12 |
13 | Posted by {{ post.author_name }} — {{ post.published_date }}
14 |
15 |
16 | {{ post.text }}
17 |
18 |
19 | {% for category in post.categories.all %}
20 | - {{ category }}
21 | {% endfor %}
22 |
23 |
24 | {% endfor %}
25 | {% endblock %}
26 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/tests.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from django.contrib.auth.models import User
3 | from django.test import TestCase
4 | from django.utils.timezone import utc
5 |
6 | from myblog.models import Category
7 | from myblog.models import Post
8 |
9 |
10 | class PostTestCase(TestCase):
11 | fixtures = ['myblog_test_fixture.json']
12 |
13 | def setUp(self):
14 | self.user = User.objects.get(pk=1)
15 |
16 | def test_string_representation(self):
17 | expected = "This is a title"
18 | p1 = Post(title=expected)
19 | actual = str(p1)
20 | self.assertEqual(expected, actual)
21 |
22 |
23 | class CategoryTestCase(TestCase):
24 |
25 | def test_string_representation(self):
26 | expected = "A Category"
27 | c1 = Category(name=expected)
28 | actual = str(c1)
29 | self.assertEqual(expected, actual)
30 |
31 |
32 | class FrontEndTestCase(TestCase):
33 | """test views provided in the front-end"""
34 | fixtures = ['myblog_test_fixture.json', ]
35 |
36 | def setUp(self):
37 | self.now = datetime.datetime.utcnow().replace(tzinfo=utc)
38 | self.timedelta = datetime.timedelta(15)
39 | author = User.objects.get(pk=1)
40 | for count in range(1, 11):
41 | post = Post(title="Post %d Title" % count,
42 | text="foo",
43 | author=author)
44 | if count < 6:
45 | # publish the first five posts
46 | pubdate = self.now - self.timedelta * count
47 | post.published_date = pubdate
48 | post.save()
49 |
50 | def test_list_only_published(self):
51 | resp = self.client.get('/')
52 | # the content of the rendered response is always a bytestring
53 | resp_text = resp.content.decode(resp.charset)
54 | self.assertTrue("Recent Posts" in resp_text)
55 | for count in range(1, 11):
56 | title = "Post %d Title" % count
57 | if count < 6:
58 | self.assertContains(resp, title, count=1)
59 | else:
60 | self.assertNotContains(resp, title)
61 |
62 | def test_details_only_published(self):
63 | for count in range(1, 11):
64 | title = "Post %d Title" % count
65 | post = Post.objects.get(title=title)
66 | resp = self.client.get('/posts/%d/' % post.pk)
67 | if count < 6:
68 | self.assertEqual(resp.status_code, 200)
69 | self.assertContains(resp, title)
70 | else:
71 | self.assertEqual(resp.status_code, 404)
72 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from myblog.views import stub_view
4 | from myblog.views import list_view
5 | from myblog.views import detail_view
6 |
7 |
8 | urlpatterns = [
9 | url(r'^$',
10 | list_view,
11 | name="blog_index"),
12 | url(r'^posts/(?P\d+)/$',
13 | detail_view,
14 | name='blog_detail'),
15 | ]
16 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/myblog/views.py:
--------------------------------------------------------------------------------
1 | from django.http import HttpResponse, HttpResponseRedirect, Http404
2 | from django.shortcuts import render
3 | from django.template import RequestContext, loader
4 |
5 | from myblog.models import Post
6 |
7 |
8 | def stub_view(request, *args, **kwargs):
9 | body = "Stub View\n\n"
10 | if args:
11 | body += "Args:\n"
12 | body += "\n".join(["\t%s" % a for a in args])
13 | if kwargs:
14 | body += "Kwargs:\n"
15 | body += "\n".join(["\t%s: %s" % i for i in kwargs.items()])
16 | return HttpResponse(body, content_type="text/plain")
17 |
18 |
19 | def list_view(request):
20 | published = Post.objects.exclude(published_date__exact=None)
21 | posts = published.order_by('-published_date')
22 | context = {'posts': posts}
23 | return render(request, 'list.html', context)
24 |
25 |
26 | def detail_view(request, post_id):
27 | published = Post.objects.exclude(published_date__exact=None)
28 | try:
29 | post = published.get(pk=post_id)
30 | except Post.DoesNotExist:
31 | raise Http404
32 | context = {'post': post}
33 | return render(request, 'detail.html', context)
34 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/mysite/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session08/mysite_stage_3/mysite/__init__.py
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/mysite/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 | My Django Blog
6 |
7 |
8 |
9 |
19 |
20 |
21 | {% block content %}
22 | [content will go here]
23 | {% endblock %}
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/mysite/templates/login.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 | My Blog Login
5 |
9 | {% endblock %}
10 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/mysite/urls.py:
--------------------------------------------------------------------------------
1 | """mysite URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.9/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: url(r'^$', 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: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Add an import: from blog import urls as blog_urls
14 | 2. Import the include() function: from django.conf.urls import url, include
15 | 3. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
16 | """
17 | from django.conf.urls import url
18 | from django.conf.urls import include
19 | from django.contrib import admin
20 | from django.contrib.auth.views import login, logout
21 |
22 | urlpatterns = [
23 | url(r'^', include('myblog.urls')),
24 | url(r'^login/$',
25 | login,
26 | {'template_name': 'login.html'},
27 | name="login"),
28 | url(r'^logout/$',
29 | logout,
30 | {'next_page': '/'},
31 | name="logout"),
32 | url(r'^admin/', admin.site.urls),
33 | ]
34 |
--------------------------------------------------------------------------------
/resources/session08/mysite_stage_3/mysite/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for mysite 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/1.9/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", "mysite.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/resources/session09/mysite/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", "mysite.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session09/mysite/myblog/__init__.py
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/admin.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from django.contrib import admin
3 | from django.core.urlresolvers import reverse
4 | from myblog.models import Post, Category
5 |
6 |
7 | class CategorizationInline(admin.TabularInline):
8 | model = Category.posts.through
9 |
10 |
11 | def make_published(modeladmin, request, queryset):
12 | now = datetime.datetime.now()
13 | queryset.update(published_date=now)
14 | make_published.short_description = "Set publication date for selected posts"
15 |
16 |
17 | class PostAdmin(admin.ModelAdmin):
18 | inlines = [
19 | CategorizationInline,
20 | ]
21 | list_display = (
22 | '__unicode__', 'author_for_admin', 'created_date', 'modified_date', 'published_date'
23 | )
24 | readonly_fields = (
25 | 'created_date', 'modified_date',
26 | )
27 | actions = [make_published, ]
28 |
29 | def author_for_admin(self, obj):
30 | author = obj.author
31 | url = reverse('admin:auth_user_change', args=(author.pk,))
32 | name = author.get_full_name() or author.username
33 | link = '{}'.format(url, name)
34 | return link
35 | author_for_admin.short_description = 'Author'
36 | author_for_admin.allow_tags = True
37 |
38 |
39 | class CategoryAdmin(admin.ModelAdmin):
40 | exclude = ('posts', )
41 |
42 |
43 | admin.site.register(Post, PostAdmin)
44 | admin.site.register(Category, CategoryAdmin)
45 |
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/fixtures/myblog_test_fixture.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pk": 1,
4 | "model": "auth.user",
5 | "fields": {
6 | "username": "admin",
7 | "first_name": "Mr.",
8 | "last_name": "Administrator",
9 | "is_active": true,
10 | "is_superuser": true,
11 | "is_staff": true,
12 | "last_login": "2013-05-24T05:35:58.628Z",
13 | "groups": [],
14 | "user_permissions": [],
15 | "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=",
16 | "email": "admin@example.com",
17 | "date_joined": "2013-05-24T05:35:58.628Z"
18 | }
19 | },
20 | {
21 | "pk": 2,
22 | "model": "auth.user",
23 | "fields": {
24 | "username": "noname",
25 | "first_name": "",
26 | "last_name": "",
27 | "is_active": true,
28 | "is_superuser": true,
29 | "is_staff": true,
30 | "last_login": "2013-05-24T05:35:58.628Z",
31 | "groups": [],
32 | "user_permissions": [],
33 | "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=",
34 | "email": "noname@example.com",
35 | "date_joined": "2013-05-24T05:35:58.628Z"
36 | }
37 | }
38 | ]
39 |
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import models, migrations
5 | from django.conf import settings
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Post',
17 | fields=[
18 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
19 | ('title', models.CharField(max_length=128)),
20 | ('text', models.TextField(blank=True)),
21 | ('created_date', models.DateTimeField(auto_now_add=True)),
22 | ('modified_date', models.DateTimeField(auto_now=True)),
23 | ('published_date', models.DateTimeField(null=True, blank=True)),
24 | ('author', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
25 | ],
26 | options={
27 | },
28 | bases=(models.Model,),
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/migrations/0002_category.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import models, migrations
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('myblog', '0001_initial'),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='Category',
16 | fields=[
17 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
18 | ('name', models.CharField(max_length=128)),
19 | ('description', models.TextField(blank=True)),
20 | ('posts', models.ManyToManyField(related_name='categories', null=True, to='myblog.Post', blank=True)),
21 | ],
22 | options={
23 | },
24 | bases=(models.Model,),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session09/mysite/myblog/migrations/__init__.py
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 |
4 |
5 | class Post(models.Model):
6 | title = models.CharField(max_length=128)
7 | text = models.TextField(blank=True)
8 | author = models.ForeignKey(User)
9 | created_date = models.DateTimeField(auto_now_add=True)
10 | modified_date = models.DateTimeField(auto_now=True)
11 | published_date = models.DateTimeField(blank=True, null=True)
12 |
13 | def __unicode__(self):
14 | return self.title
15 |
16 |
17 | class Category(models.Model):
18 | name = models.CharField(max_length=128)
19 | description = models.TextField(blank=True)
20 | posts = models.ManyToManyField(Post, blank=True, null=True,
21 | related_name='categories')
22 |
23 | class Meta:
24 | verbose_name_plural = 'Categories'
25 |
26 | def __unicode__(self):
27 | return self.name
28 |
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/static/django_blog.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #eee;
3 | color: #111;
4 | font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
5 | margin:0;
6 | padding:0;
7 | }
8 | #container {
9 | margin:0;
10 | padding:0;
11 | margin-top: 0px;
12 | }
13 | #header {
14 | background-color: #333;
15 | border-botton: 1px solid #111;
16 | margin:0;
17 | padding:0;
18 | }
19 | #control-bar {
20 | margin: 0em 0em 1em;
21 | list-style: none;
22 | list-style-type: none;
23 | text-align: right;
24 | color: #eee;
25 | font-size: 80%;
26 | padding-bottom: 0.4em;
27 | }
28 | #control-bar li {
29 | display: inline-block;
30 | }
31 | #control-bar li a {
32 | color: #eee;
33 | padding: 0.5em;
34 | text-decoration: none;
35 | }
36 | #control-bar li a:hover {
37 | color: #cce;
38 | }
39 | #content {
40 | margin: 0em 1em 1em;
41 | }
42 |
43 | ul#entries {
44 | list-style: none;
45 | list-style-type: none;
46 | }
47 | div.entry {
48 | margin-right: 2em;
49 | margin-top: 1em;
50 | border-top: 1px solid #cecece;
51 | }
52 | ul#entries li:first-child div.entry {
53 | border-top: none;
54 | margin-top: 0em;
55 | }
56 | div.entry-body {
57 | margin-left: 2em;
58 | }
59 | .notification {
60 | float: right;
61 | text-align: center;
62 | width: 25%;
63 | padding: 1em;
64 | }
65 | .info {
66 | background-color: #aae;
67 | }
68 | ul.categories {
69 | list-style: none;
70 | list-style-type: none;
71 | }
72 | ul.categories li {
73 | display: inline;
74 | }
75 |
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/templates/detail.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 | Home
5 | {{ post }}
6 |
7 | Posted by {{ post.author_name }} — {{ post.published_date }}
8 |
9 |
10 | {{ post.text }}
11 |
12 |
13 | {% for category in post.categories.all %}
14 | - {{ category }}
15 | {% endfor %}
16 |
17 | {% endblock %}
18 |
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/templates/list.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 | Recent Posts
5 |
6 | {% comment %} here is where the query happens {% endcomment %}
7 | {% for post in posts %}
8 |
9 |
12 |
13 | Posted by {{ post.author_name }} — {{ post.published_date }}
14 |
15 |
16 | {{ post.text }}
17 |
18 |
19 | {% for category in post.categories.all %}
20 | - {{ category }}
21 | {% endfor %}
22 |
23 |
24 | {% endfor %}
25 | {% endblock %}
26 |
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/tests.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from django.test import TestCase
3 | from django.contrib.auth.models import User
4 | from django.utils.timezone import utc
5 | from myblog.models import Post, Category
6 |
7 |
8 | class PostTestCase(TestCase):
9 | fixtures = ['myblog_test_fixture.json', ]
10 |
11 | def setUp(self):
12 | self.user = User.objects.get(pk=1)
13 |
14 | def test_unicode(self):
15 | expected = u"This is a title"
16 | p1 = Post(title=expected)
17 | actual = unicode(p1)
18 | self.assertEqual(expected, actual)
19 |
20 |
21 | class CategoryTestCase(TestCase):
22 |
23 | def test_unicode(self):
24 | expected = "A Category"
25 | c1 = Category(name=expected)
26 | actual = unicode(c1)
27 | self.assertEqual(expected, actual)
28 |
29 |
30 | class FrontEndTestCase(TestCase):
31 | """test views provided in the front-end"""
32 | fixtures = ['myblog_test_fixture.json', ]
33 |
34 | def setUp(self):
35 | self.now = datetime.datetime.utcnow().replace(tzinfo=utc)
36 | self.timedelta = datetime.timedelta(15)
37 | author = User.objects.get(pk=1)
38 | for count in range(1, 11):
39 | post = Post(title="Post %d Title" % count,
40 | text="foo",
41 | author=author)
42 | if count < 6:
43 | # publish the first five posts
44 | pubdate = self.now - self.timedelta * count
45 | post.published_date = pubdate
46 | post.save()
47 |
48 | def test_list_only_published(self):
49 | resp = self.client.get('/')
50 | self.assertTrue("Recent Posts" in resp.content)
51 | for count in range(1, 11):
52 | title = "Post %d Title" % count
53 | if count < 6:
54 | self.assertContains(resp, title, count=1)
55 | else:
56 | self.assertNotContains(resp, title)
57 |
58 | def test_details_only_published(self):
59 | for count in range(1, 11):
60 | title = "Post %d Title" % count
61 | post = Post.objects.get(title=title)
62 | resp = self.client.get('/posts/%d/' % post.pk)
63 | if count < 6:
64 | self.assertEqual(resp.status_code, 200)
65 | self.assertContains(resp, title)
66 | else:
67 | self.assertEqual(resp.status_code, 404)
68 |
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, url
2 |
3 |
4 | urlpatterns = patterns(
5 | 'myblog.views',
6 | url(r'^$',
7 | 'list_view',
8 | name="blog_index"),
9 | url(r'^posts/(?P\d+)/$',
10 | 'detail_view',
11 | name="blog_detail"),
12 | )
13 |
--------------------------------------------------------------------------------
/resources/session09/mysite/myblog/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from django.http import HttpResponse, HttpResponseRedirect, Http404
3 | from django.template import RequestContext, loader
4 | from myblog.models import Post
5 |
6 |
7 | def stub_view(request, *args, **kwargs):
8 | body = "Stub View\n\n"
9 | if args:
10 | body += "Args:\n"
11 | body += "\n".join(["\t%s" % a for a in args])
12 | if kwargs:
13 | body += "Kwargs:\n"
14 | body += "\n".join(["\t%s: %s" % i for i in kwargs.items()])
15 | return HttpResponse(body, content_type="text/plain")
16 |
17 |
18 | def list_view(request):
19 | published = Post.objects.exclude(published_date__exact=None)
20 | posts = published.order_by('-published_date')
21 | context = {'posts': posts}
22 | return render(request, 'list.html', context)
23 |
24 |
25 | def detail_view(request, post_id):
26 | published = Post.objects.exclude(published_date__exact=None)
27 | try:
28 | post = published.get(pk=post_id)
29 | except Post.DoesNotExist:
30 | raise Http404
31 | context = {'post': post}
32 | return render(request, 'detail.html', context)
33 |
--------------------------------------------------------------------------------
/resources/session09/mysite/mysite/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/resources/session09/mysite/mysite/__init__.py
--------------------------------------------------------------------------------
/resources/session09/mysite/mysite/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for mysite project.
3 |
4 | For more information on this file, see
5 | https://docs.djangoproject.com/en/1.7/topics/settings/
6 |
7 | For the full list of settings and their values, see
8 | https://docs.djangoproject.com/en/1.7/ref/settings/
9 | """
10 |
11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
12 | import os
13 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
14 |
15 |
16 | # Quick-start development settings - unsuitable for production
17 | # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
18 |
19 | # SECURITY WARNING: keep the secret key used in production secret!
20 | SECRET_KEY = 'e@3=0i!#n4l25r*ul*sbx6b$@gh7a6pjee6lr-slw9!ayj#*@f'
21 |
22 | # SECURITY WARNING: don't run with debug turned on in production!
23 | DEBUG = True
24 |
25 | TEMPLATE_DEBUG = True
26 |
27 | ALLOWED_HOSTS = []
28 |
29 |
30 | # Application definition
31 |
32 | INSTALLED_APPS = (
33 | 'django.contrib.admin',
34 | 'django.contrib.auth',
35 | 'django.contrib.contenttypes',
36 | 'django.contrib.sessions',
37 | 'django.contrib.messages',
38 | 'django.contrib.staticfiles',
39 | 'myblog',
40 | )
41 |
42 | MIDDLEWARE_CLASSES = (
43 | 'django.contrib.sessions.middleware.SessionMiddleware',
44 | 'django.middleware.common.CommonMiddleware',
45 | 'django.middleware.csrf.CsrfViewMiddleware',
46 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
47 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
48 | 'django.contrib.messages.middleware.MessageMiddleware',
49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
50 | )
51 |
52 | ROOT_URLCONF = 'mysite.urls'
53 |
54 | WSGI_APPLICATION = 'mysite.wsgi.application'
55 |
56 |
57 | # Database
58 | # https://docs.djangoproject.com/en/1.7/ref/settings/#databases
59 |
60 | DATABASES = {
61 | 'default': {
62 | 'ENGINE': 'django.db.backends.sqlite3',
63 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
64 | }
65 | }
66 |
67 | # Internationalization
68 | # https://docs.djangoproject.com/en/1.7/topics/i18n/
69 |
70 | LANGUAGE_CODE = 'en-us'
71 |
72 | TIME_ZONE = 'UTC'
73 |
74 | USE_I18N = True
75 |
76 | USE_L10N = True
77 |
78 | USE_TZ = True
79 |
80 |
81 | # Static files (CSS, JavaScript, Images)
82 | # https://docs.djangoproject.com/en/1.7/howto/static-files/
83 |
84 | STATIC_URL = '/static/'
85 |
86 |
87 | TEMPLATE_DIRS = (os.path.join(BASE_DIR, 'mysite/templates'), )
88 | LOGIN_URL = '/login/'
89 | LOGIN_REDIRECT_URL = '/'
90 |
--------------------------------------------------------------------------------
/resources/session09/mysite/mysite/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 | My Django Blog
6 |
7 |
8 |
9 |
21 |
22 |
23 | {% block content %}
24 | [content will go here]
25 | {% endblock %}
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/resources/session09/mysite/mysite/templates/login.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 | My Blog Login
5 |
9 | {% endblock %}
10 |
--------------------------------------------------------------------------------
/resources/session09/mysite/mysite/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 | from django.contrib import admin
3 |
4 | urlpatterns = patterns('',
5 | url(r'^', include('myblog.urls')),
6 | url(r'^login/$',
7 | 'django.contrib.auth.views.login',
8 | {'template_name': 'login.html'},
9 | name="login"),
10 | url(r'^logout/$',
11 | 'django.contrib.auth.views.logout',
12 | {'next_page': '/'},
13 | name="logout"),
14 | url(r'^admin/', include(admin.site.urls)),
15 | )
16 |
--------------------------------------------------------------------------------
/resources/session09/mysite/mysite/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for mysite 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/1.7/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
12 |
13 | from django.core.wsgi import get_wsgi_application
14 | application = get_wsgi_application()
15 |
--------------------------------------------------------------------------------
/source/_static/admin_index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/admin_index.png
--------------------------------------------------------------------------------
/source/_static/apache.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/apache.png
--------------------------------------------------------------------------------
/source/_static/bike.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/bike.jpg
--------------------------------------------------------------------------------
/source/_static/bluebox_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/bluebox_logo.png
--------------------------------------------------------------------------------
/source/_static/by-nc-sa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/by-nc-sa.png
--------------------------------------------------------------------------------
/source/_static/cgitb_output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/cgitb_output.png
--------------------------------------------------------------------------------
/source/_static/cloud_cover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/cloud_cover.jpg
--------------------------------------------------------------------------------
/source/_static/data_flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/data_flow.png
--------------------------------------------------------------------------------
/source/_static/data_in_tcpip_stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/data_in_tcpip_stack.png
--------------------------------------------------------------------------------
/source/_static/django-admin-login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/django-admin-login.png
--------------------------------------------------------------------------------
/source/_static/django-pony.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/django-pony.png
--------------------------------------------------------------------------------
/source/_static/django-start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/django-start.png
--------------------------------------------------------------------------------
/source/_static/django_lead.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/django_lead.png
--------------------------------------------------------------------------------
/source/_static/flask_cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/flask_cover.png
--------------------------------------------------------------------------------
/source/_static/flask_full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/flask_full.png
--------------------------------------------------------------------------------
/source/_static/flask_hello.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/flask_hello.png
--------------------------------------------------------------------------------
/source/_static/flask_square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/flask_square.png
--------------------------------------------------------------------------------
/source/_static/forbidden.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/forbidden.png
--------------------------------------------------------------------------------
/source/_static/framework_quote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/framework_quote.png
--------------------------------------------------------------------------------
/source/_static/gateway.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/gateway.jpg
--------------------------------------------------------------------------------
/source/_static/geojson-io.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/geojson-io.png
--------------------------------------------------------------------------------
/source/_static/granny_mashup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/granny_mashup.png
--------------------------------------------------------------------------------
/source/_static/heroku-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/heroku-logo.png
--------------------------------------------------------------------------------
/source/_static/icup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/icup.png
--------------------------------------------------------------------------------
/source/_static/learning_journal_styled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/learning_journal_styled.png
--------------------------------------------------------------------------------
/source/_static/lj_entry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/lj_entry.png
--------------------------------------------------------------------------------
/source/_static/logo_UW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/logo_UW.png
--------------------------------------------------------------------------------
/source/_static/mac-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/mac-icon.png
--------------------------------------------------------------------------------
/source/_static/mod_wsgi_flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/mod_wsgi_flow.png
--------------------------------------------------------------------------------
/source/_static/network_topology.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/network_topology.png
--------------------------------------------------------------------------------
/source/_static/nginx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/nginx.png
--------------------------------------------------------------------------------
/source/_static/nginx_hello.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/nginx_hello.png
--------------------------------------------------------------------------------
/source/_static/no_entry.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/no_entry.jpg
--------------------------------------------------------------------------------
/source/_static/plone-icon-256-white-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/plone-icon-256-white-bg.png
--------------------------------------------------------------------------------
/source/_static/plone_conf_2012.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/plone_conf_2012.jpg
--------------------------------------------------------------------------------
/source/_static/protocol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/protocol.png
--------------------------------------------------------------------------------
/source/_static/protocol_sea.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/protocol_sea.png
--------------------------------------------------------------------------------
/source/_static/proxy_wsgi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/proxy_wsgi.png
--------------------------------------------------------------------------------
/source/_static/pyramid-medium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/pyramid-medium.png
--------------------------------------------------------------------------------
/source/_static/python.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/python.png
--------------------------------------------------------------------------------
/source/_static/scream.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/scream.jpg
--------------------------------------------------------------------------------
/source/_static/sheep_pyramid.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/sheep_pyramid.jpg
--------------------------------------------------------------------------------
/source/_static/skateboard.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/skateboard.jpg
--------------------------------------------------------------------------------
/source/_static/socket_interaction.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/socket_interaction.png
--------------------------------------------------------------------------------
/source/_static/wiki_frontpage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/wiki_frontpage.png
--------------------------------------------------------------------------------
/source/_static/wsgi_middleware_onion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/wsgi_middleware_onion.png
--------------------------------------------------------------------------------
/source/_static/wsgiref_flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_static/wsgiref_flow.png
--------------------------------------------------------------------------------
/source/_templates/end_slide.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Good Night!
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/source/_templates/title_slide.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/end_slide.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | <Thank You!>
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/slide.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ title }}
4 |
5 |
6 | {{ content }}
7 |
8 | {% if config.slide_numbers %}
9 | {{ slide_number }}
10 | {% endif %}
11 | {% if config.slide_footer %}
12 |
13 | {% endif %}
14 |
15 |
16 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/0fe24dfc41fffed2d6891c797fcd7dee100afa65/_hacks.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/0fe24dfc41fffed2d6891c797fcd7dee100afa65/_hacks.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_background-size.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_background-size.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_border-radius.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_border-radius.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_box-shadow.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_box-shadow.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_box-sizing.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_box-sizing.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_box.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_box.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_columns.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_columns.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_deprecated-support.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_deprecated-support.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_images.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_images.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_text-shadow.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_text-shadow.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_transform.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_transform.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_transition.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_transition.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_user-interface.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/17d03613c125918cd0766f51918feb21dc3c074a/_user-interface.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/65e4c30c131f260ea88c3e4f2e16dfc2ba547e74/_utilities.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/65e4c30c131f260ea88c3e4f2e16dfc2ba547e74/_utilities.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/_base.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/_base.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/_variables.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/_variables.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/default.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/default.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/hieroglyph.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/hieroglyph.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/io2013.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/io2013.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/phone.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/a3714e1b6bb8b987fa4c7e14e1704c7e18bb1783/phone.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/af1a5722249b61fe97c5776f7a26e0902a00f406/_reset.scssc:
--------------------------------------------------------------------------------
1 | 3.4.13 (Selective Steve)
2 | df78759f0fe6b88a633d20d26581ca4cdb829111
3 | o:Sass::Tree::RootNode:@children[o:Sass::Tree::ImportNode:@imported_filenameI"reset/utilities:ET;[ :@template0:
4 | @linei:@source_rangeo:Sass::Source::Range :@start_poso:Sass::Source::Position;i:@offseti:
@end_poso;;i;i:
5 | @fileI"U/Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/_reset.scss; T:@importero: Sass::Importers::Filesystem:
6 | @rootI"A/Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets; F:@real_rootI"A/Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets; F:@same_name_warningso:Set:
7 | @hash{ :
@options{ :@imported_file0o:Sass::Tree::MixinNode:
8 | @nameI"global-reset; T:
9 | @args[ :@keywords{ :@splat0:@kwarg_splat0;[ ;i;o;
;o;;i;i;o;;i;i;@
;@;@;
10 | I"8@import "reset/utilities";
11 |
12 | @include global-reset;
13 | ; T;i;o;
;o;;i;i;o;;i;i;@
;@:@has_childrenT;@
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/.sass-cache/af1a5722249b61fe97c5776f7a26e0902a00f406/_support.scssc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/.sass-cache/af1a5722249b61fe97c5776f7a26e0902a00f406/_support.scssc
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/config.rb:
--------------------------------------------------------------------------------
1 | # Require any additional compass plugins here.
2 |
3 | # Set this to the root of your project when deployed:
4 | http_path = "/"
5 | css_dir = "theme/css"
6 | sass_dir = "theme/scss"
7 | images_dir = "images"
8 | javascripts_dir = "js"
9 |
10 | # You can select your preferred output style here (can be overridden via the command line):
11 | output_style = :expanded #:expanded or :nested or :compact or :compressed
12 |
13 | # To enable relative paths to assets via compass helper functions. Uncomment:
14 | # relative_assets = true
15 |
16 | # To disable debugging comments that display the original location of your selectors. Uncomment:
17 | # line_comments = false
18 |
19 |
20 | # If you prefer the indented syntax, you might want to regenerate this
21 | # project again passing --syntax sass, or you can uncomment this:
22 | # preferred_syntax = :sass
23 | # and then run:
24 | # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
25 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/order.js:
--------------------------------------------------------------------------------
1 | /*
2 | RequireJS order 1.0.5 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
3 | Available via the MIT or new BSD license.
4 | see: http://github.com/jrburke/requirejs for details
5 | */
6 | (function(){function k(a){var b=a.currentTarget||a.srcElement,c;if(a.type==="load"||l.test(b.readyState)){a=b.getAttribute("data-requiremodule");j[a]=!0;for(a=0;c=g[a];a++)if(j[c.name])c.req([c.name],c.onLoad);else break;a>0&&g.splice(0,a);setTimeout(function(){b.parentNode.removeChild(b)},15)}}function m(a){var b,c;a.setAttribute("data-orderloaded","loaded");for(a=0;c=h[a];a++)if((b=i[c])&&b.getAttribute("data-orderloaded")==="loaded")delete i[c],require.addScriptToDom(b);else break;a>0&&h.splice(0,
7 | a)}var f=typeof document!=="undefined"&&typeof window!=="undefined"&&document.createElement("script"),n=f&&(f.async||window.opera&&Object.prototype.toString.call(window.opera)==="[object Opera]"||"MozAppearance"in document.documentElement.style),o=f&&f.readyState==="uninitialized",l=/^(complete|loaded)$/,g=[],j={},i={},h=[],f=null;define({version:"1.0.5",load:function(a,b,c,e){var d;b.nameToUrl?(d=b.nameToUrl(a,null),require.s.skipAsync[d]=!0,n||e.isBuild?b([a],c):o?(e=require.s.contexts._,!e.urlFetched[d]&&
8 | !e.loaded[a]&&(e.urlFetched[d]=!0,require.resourcesReady(!1),e.scriptCount+=1,d=require.attach(d,e,a,null,null,m),i[a]=d,h.push(a)),b([a],c)):b.specified(a)?b([a],c):(g.push({name:a,req:b,onLoad:c}),require.attach(d,null,a,k,"script/cache"))):b([a],c)}})})();
9 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/polyfills/classList.min.js:
--------------------------------------------------------------------------------
1 | /* @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
2 | "use strict";if(typeof document!=="undefined"&&!("classList" in document.createElement("a"))){(function(a){var f="classList",d="prototype",e=(a.HTMLElement||a.Element)[d],g=Object;strTrim=String[d].trim||function(){return this.replace(/^\s+|\s+$/g,"")},arrIndexOf=Array[d].indexOf||function(k){for(var j=0,h=this.length;j)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);
3 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-go.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/js/prettify/lang-go.js
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-hs.js:
--------------------------------------------------------------------------------
1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^\n\f\r'\\]|\\[^&])'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/,
2 | null],["pln",/^(?:[A-Z][\w']*\.)*[A-Za-z][\w']*/],["pun",/^[^\d\t-\r "'A-Za-z]+/]]),["hs"]);
3 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-lisp.js:
--------------------------------------------------------------------------------
1 | var a=null;
2 | PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,a,"("],["clo",/^\)+/,a,")"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,a],
3 | ["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["cl","el","lisp","scm"]);
4 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-lua.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/js/prettify/lang-lua.js
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-ml.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/js/prettify/lang-ml.js
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-n.js:
--------------------------------------------------------------------------------
1 | var a=null;
2 | PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:'(?:[^\n\r'\\]|\\.)*'|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,a,'"'],["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,a,"#"],["pln",/^\s+/,a," \r\n\t\xa0"]],[["str",/^@"(?:[^"]|"")*(?:"|$)/,a],["str",/^<#[^#>]*(?:#>|$)/,a],["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,a],["com",/^\/\/[^\n\r]*/,a],["com",/^\/\*[\S\s]*?(?:\*\/|$)/,
3 | a],["kwd",/^(?:abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield)\b/,
4 | a],["typ",/^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\b/,a],["lit",/^@[$_a-z][\w$@]*/i,a],["typ",/^@[A-Z]+[a-z][\w$@]*/,a],["pln",/^'?[$_a-z][\w$@]*/i,a],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,a,"0123456789"],["pun",/^.[^\s\w"-$'./@`]*/,a]]),["n","nemerle"]);
5 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-proto.js:
--------------------------------------------------------------------------------
1 | PR.registerLangHandler(PR.sourceDecorator({keywords:"bytes,default,double,enum,extend,extensions,false,group,import,max,message,option,optional,package,repeated,required,returns,rpc,service,syntax,to,true",types:/^(bool|(double|s?fixed|[su]?int)(32|64)|float|string)\b/,cStyleComments:!0}),["proto"]);
2 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-scala.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/js/prettify/lang-scala.js
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-sql.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/js/prettify/lang-sql.js
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-tex.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/js/prettify/lang-tex.js
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-vb.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/js/prettify/lang-vb.js
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-vhdl.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/js/prettify/lang-vhdl.js
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-wiki.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/static/js/prettify/lang-wiki.js
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/lang-yaml.js:
--------------------------------------------------------------------------------
1 | var a=null;
2 | PR.registerLangHandler(PR.createSimpleLexer([["pun",/^[:>?|]+/,a,":|>?"],["dec",/^%(?:YAML|TAG)[^\n\r#]+/,a,"%"],["typ",/^&\S+/,a,"&"],["typ",/^!\S*/,a,"!"],["str",/^"(?:[^"\\]|\\.)*(?:"|$)/,a,'"'],["str",/^'(?:[^']|'')*(?:'|$)/,a,"'"],["com",/^#[^\n\r]*/,a,"#"],["pln",/^\s+/,a," \t\r\n"]],[["dec",/^(?:---|\.\.\.)(?:[\n\r]|$)/],["pun",/^-/],["kwd",/^\w+:[\n\r ]/],["pln",/^\w+/]]),["yaml","yml"]);
3 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/prettify/prettify.css:
--------------------------------------------------------------------------------
1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/slide-deck-instantiate.js:
--------------------------------------------------------------------------------
1 |
2 | // Polyfill missing APIs (if we need to), then create the slide deck.
3 | // iOS < 5 needs classList, dataset, and window.matchMedia. Modernizr contains
4 | // the last one.
5 | (function() {
6 | Modernizr.load({
7 | test: !!document.body.classList && !!document.body.dataset,
8 | nope: ['js/polyfills/classList.min.js', 'js/polyfills/dataset.min.js'],
9 | complete: function() {
10 | window.slidedeck = new SlideDeck();
11 | }
12 | });
13 | })();
14 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/slide-testing.js:
--------------------------------------------------------------------------------
1 | require(['order!modernizr.custom.45394',
2 | 'order!prettify/prettify', 'order!hammer', 'order!slide-controller',
3 | 'order!slide-deck',
4 | 'order!slide-deck-instantiate'], function(someModule) {
5 |
6 | });
7 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/js/slides.js:
--------------------------------------------------------------------------------
1 | require(['order!../slide_config', 'order!modernizr.custom.45394',
2 | 'order!prettify/prettify', 'order!hammer', 'order!slide-controller',
3 | 'order!slide-deck',
4 | 'order!slide-deck-instantiate'], function(someModule) {
5 |
6 | });
7 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/scripts/md/README.md:
--------------------------------------------------------------------------------
1 | ### Want to use markdown to write your slides?
2 |
3 | `python render.py` can do that for you.
4 |
5 | Dependencies: jinja2, markdown.
6 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/scripts/md/render.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import codecs
4 | import re
5 | import jinja2
6 | import markdown
7 |
8 | def process_slides():
9 | with codecs.open('../../presentation-output.html', 'w', encoding='utf8') as outfile:
10 | md = codecs.open('slides.md', encoding='utf8').read()
11 | md_slides = md.split('\n---\n')
12 | print 'Compiled %s slides.' % len(md_slides)
13 |
14 | slides = []
15 | # Process each slide separately.
16 | for md_slide in md_slides:
17 | slide = {}
18 | sections = md_slide.split('\n\n')
19 | # Extract metadata at the beginning of the slide (look for key: value)
20 | # pairs.
21 | metadata_section = sections[0]
22 | metadata = parse_metadata(metadata_section)
23 | slide.update(metadata)
24 | remainder_index = metadata and 1 or 0
25 | # Get the content from the rest of the slide.
26 | content_section = '\n\n'.join(sections[remainder_index:])
27 | html = markdown.markdown(content_section)
28 | slide['content'] = postprocess_html(html, metadata)
29 |
30 | slides.append(slide)
31 |
32 | template = jinja2.Template(open('base.html').read())
33 |
34 | outfile.write(template.render(locals()))
35 |
36 | def parse_metadata(section):
37 | """Given the first part of a slide, returns metadata associated with it."""
38 | metadata = {}
39 | metadata_lines = section.split('\n')
40 | for line in metadata_lines:
41 | colon_index = line.find(':')
42 | if colon_index != -1:
43 | key = line[:colon_index].strip()
44 | val = line[colon_index + 1:].strip()
45 | metadata[key] = val
46 |
47 | return metadata
48 |
49 | def postprocess_html(html, metadata):
50 | """Returns processed HTML to fit into the slide template format."""
51 | if metadata.get('build_lists') and metadata['build_lists'] == 'true':
52 | html = html.replace('', '')
53 | html = html.replace('', '')
54 | return html
55 |
56 | if __name__ == '__main__':
57 | process_slides()
58 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/scripts/md/slides.md:
--------------------------------------------------------------------------------
1 | title: Slide Title
2 | subtitle: Subtitle
3 | class: image
4 |
5 | 
6 |
7 | ---
8 |
9 | title: Segue Slide
10 | subtitle: Subtitle
11 | class: segue dark nobackground
12 |
13 | ---
14 |
15 | title: Agenda
16 | class: big
17 | build_lists: true
18 |
19 | Things we'll cover (list should build):
20 |
21 | - Bullet1
22 | - Bullet2
23 | - Bullet3
24 |
25 | ---
26 |
27 | title: Today
28 | class: nobackground fill
29 |
30 | 
31 |
32 |
33 |
34 | ---
35 |
36 | title: Big Title Slide
37 | class: title-slide
38 |
39 | ---
40 |
41 | title: Code Example
42 |
43 | Media Queries are sweet:
44 |
45 |
46 | @media screen and (max-width: 640px) {
47 | #sidebar { display: none; }
48 | }
49 |
50 |
51 | ---
52 |
53 | title: Once more, with JavaScript
54 |
55 |
56 | function isSmall() {
57 | return window.matchMedia("(min-device-width: ???)").matches;
58 | }
59 |
60 | function hasTouch() {
61 | return Modernizr.touch;
62 | }
63 |
64 | function detectFormFactor() {
65 | var device = DESKTOP;
66 | if (hasTouch()) {
67 | device = isSmall() ? PHONE : TABLET;
68 | }
69 | return device;
70 | }
71 |
72 |
73 | ---
74 |
75 | title: Centered content
76 | content_class: flexbox vcenter
77 |
78 | This content should be centered!
79 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/slide_config.js:
--------------------------------------------------------------------------------
1 | var SLIDE_CONFIG = {
2 | // Slide settings
3 | settings: {
4 | title: 'Title Goes Here
Up To Two Lines',
5 | subtitle: 'Subtitle Goes Here',
6 | //eventInfo: {
7 | // title: 'Google I/O',
8 | // date: '6/x/2013'
9 | //},
10 | useBuilds: true, // Default: true. False will turn off slide animation builds.
11 | usePrettify: true, // Default: true
12 | enableSlideAreas: true, // Default: true. False turns off the click areas on either slide of the slides.
13 | enableTouch: true, // Default: true. If touch support should enabled. Note: the device must support touch.
14 | //analytics: 'UA-XXXXXXXX-1', // TODO: Using this breaks GA for some reason (probably requirejs). Update your tracking code in template.html instead.
15 | favIcon: 'images/google_developers_logo_tiny.png',
16 | fonts: [
17 | 'Open Sans:regular,semibold,italic,italicsemibold',
18 | 'Source Code Pro'
19 | ],
20 | //theme: ['mytheme'], // Add your own custom themes or styles in /theme/css. Leave off the .css extension.
21 | },
22 |
23 | // Author information
24 | presenters: [{
25 | name: 'Firstname Lastname',
26 | company: 'Job Title
Google',
27 | gplus: 'http://plus.google.com/1234567890',
28 | twitter: '@yourhandle',
29 | www: 'http://www.you.com',
30 | github: 'http://github.com/you'
31 | }/*, {
32 | name: 'Second Name',
33 | company: 'Job Title, Google',
34 | gplus: 'http://plus.google.com/1234567890',
35 | twitter: '@yourhandle',
36 | www: 'http://www.you.com',
37 | github: 'http://github.com/you'
38 | }*/]
39 | };
40 |
41 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/slide_config.js_t:
--------------------------------------------------------------------------------
1 | var SLIDE_CONFIG = {
2 | // Slide settings
3 | settings: {
4 | title: '{{ docstitle|e }}',
5 | subtitle: '{{ theme_subtitle|e }}',
6 | //eventInfo: {
7 | // title: 'Google I/O',
8 | // date: '6/x/2013'
9 | //},
10 | useBuilds: {{ theme_use_builds }}, // Default: true. False will turn off slide animation builds.
11 | usePrettify: {{ theme_use_prettify }}, // Default: true
12 | enableSlideAreas: {{ theme_enable_slide_areas }}, // Default: true. False turns off the click areas on either slide of the slides.
13 | enableTouch: {{ theme_enable_touch }}, // Default: true. If touch support should enabled. Note: the device must support touch.
14 | //analytics: 'UA-XXXXXXXX-1', // TODO: Using this breaks GA for some reason (probably requirejs). Update your tracking code in template.html instead.
15 | favIcon: {{ theme_favicon }},
16 | fonts: [
17 | 'Open Sans:regular,semibold,italic,italicsemibold',
18 | 'Source Code Pro'
19 | ],
20 | //theme: ['mytheme'], // Add your own custom themes or styles in /theme/css. Leave off the .css extension.
21 | },
22 |
23 | // Author information
24 | presenters: {% if theme_presenters %}{{ theme_presenters|json }}
25 | {% else %}[]
26 | {% endif %}
27 | };
28 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/theme/css/hieroglyph.css:
--------------------------------------------------------------------------------
1 | /* line 5, ../scss/hieroglyph.scss */
2 | ol {
3 | margin-left: 1.2em;
4 | margin-bottom: 1em;
5 | position: relative;
6 | list-style: decimal;
7 | }
8 | /* line 11, ../scss/hieroglyph.scss */
9 | ol li {
10 | margin-bottom: 0.5em;
11 | }
12 | /* line 14, ../scss/hieroglyph.scss */
13 | ol li ol {
14 | margin-left: 2em;
15 | margin-bottom: 0;
16 | list-style: decimal;
17 | }
18 | /* line 19, ../scss/hieroglyph.scss */
19 | ol li ol li:before {
20 | font-weight: 600;
21 | }
22 | /* line 25, ../scss/hieroglyph.scss */
23 | ol ol {
24 | margin-top: .5em;
25 | list-style: decimal;
26 | }
27 |
28 | /* line 32, ../scss/hieroglyph.scss */
29 | slide.title-image {
30 | padding-right: 0px;
31 | }
32 | /* line 36, ../scss/hieroglyph.scss */
33 | slide.title-image hgroup {
34 | position: static !important;
35 | margin-top: 35%;
36 | padding-left: 30px;
37 | background: rgba(255, 255, 255, 0.7);
38 | border-top-left-radius: 5px;
39 | -webkit-border-top-left-radius: 5px;
40 | -moz-border-top-left-radius: 5px;
41 | -o-border-top-left-radius: 5px;
42 | }
43 | /* line 50, ../scss/hieroglyph.scss */
44 | slide.title-image hgroup + article {
45 | background: rgba(255, 255, 255, 0.7);
46 | margin-top: 0px;
47 | padding-left: 30px;
48 | border-bottom-left-radius: 5px;
49 | -webkit-border-bottom-left-radius: 5px;
50 | -moz-border-bottom-left-radius: 5px;
51 | -o-border-bottom-left-radius: 5px;
52 | }
53 | /* line 62, ../scss/hieroglyph.scss */
54 | slide.title-image h1 {
55 | color: #222;
56 | font-size: 3.2em;
57 | line-height: 1.5em;
58 | font-weight: 500;
59 | }
60 | /* line 72, ../scss/hieroglyph.scss */
61 | slide.title-image div.figure img {
62 | position: absolute;
63 | left: 0;
64 | top: 0;
65 | min-width: 100%;
66 | min-height: 100%;
67 | border-radius: 5px;
68 | -o-border-radius: 5px;
69 | -moz-border-radius: 5px;
70 | -webkit-border-radius: 5px;
71 | z-index: -1;
72 | }
73 | /* line 87, ../scss/hieroglyph.scss */
74 | slide.title-image div.figure .caption {
75 | color: black;
76 | background: rgba(255, 255, 255, 0.25);
77 | padding: 0 5px;
78 | border-bottom-left-radius: 5px;
79 | border-top-right-radius: 5px;
80 | position: absolute;
81 | left: 0;
82 | bottom: 0;
83 | margin-bottom: 0;
84 | }
85 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/theme/css/io2013.css:
--------------------------------------------------------------------------------
1 | /* line 5, ../scss/io2013.scss */
2 | * {
3 | line-height: 1.3;
4 | }
5 |
6 | /* line 9, ../scss/io2013.scss */
7 | h2 {
8 | font-weight: bold;
9 | }
10 |
11 | /* line 12, ../scss/io2013.scss */
12 | h2, h3 {
13 | color: #515151;
14 | }
15 |
16 | /* line 16, ../scss/io2013.scss */
17 | q, blockquote {
18 | font-weight: bold;
19 | }
20 |
21 | /* line 20, ../scss/io2013.scss */
22 | slides > slide {
23 | color: #515151;
24 | }
25 | /* line 24, ../scss/io2013.scss */
26 | slides > slide.title-slide:after {
27 | content: '';
28 | background: url(../../images/io2013/google-io-lockup-1.png) no-repeat 100% 50%;
29 | -moz-background-size: contain;
30 | -o-background-size: contain;
31 | -webkit-background-size: contain;
32 | background-size: contain;
33 | position: absolute;
34 | bottom: 80px;
35 | right: 40px;
36 | width: 100%;
37 | height: 90px;
38 | }
39 | /* line 36, ../scss/io2013.scss */
40 | slides > slide.title-slide hgroup h1 {
41 | font-weight: bold;
42 | line-height: 1.1;
43 | }
44 | /* line 40, ../scss/io2013.scss */
45 | slides > slide.title-slide hgroup h2, slides > slide.title-slide hgroup p {
46 | color: #515151;
47 | }
48 | /* line 43, ../scss/io2013.scss */
49 | slides > slide.title-slide hgroup h2 {
50 | margin-top: 0.25em;
51 | }
52 | /* line 46, ../scss/io2013.scss */
53 | slides > slide.title-slide hgroup p {
54 | margin-top: 3em;
55 | }
56 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/theme/css/phone.css:
--------------------------------------------------------------------------------
1 | /*Smartphones (portrait and landscape) ----------- */
2 | /*@media only screen
3 | and (min-width : 320px)
4 | and (max-width : 480px) {
5 |
6 | }*/
7 | /* Smartphones (portrait) ----------- */
8 | /* Styles */
9 | /* line 17, ../scss/phone.scss */
10 | slides > slide {
11 | /* width: $slide-width !important;
12 | height: $slide-height !important;
13 | margin-left: -$slide-width / 2 !important;
14 | margin-top: -$slide-height / 2 !important;
15 | */
16 | -webkit-transition: none !important;
17 | -moz-transition: none !important;
18 | -o-transition: none !important;
19 | -webkit-transition: none !important;
20 | transition: none !important;
21 | }
22 |
23 | /* iPhone 4 ----------- */
24 | @media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) {
25 | /* Styles */
26 | }
27 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/theme/scss/_variables.scss:
--------------------------------------------------------------------------------
1 | $social-tags: '';
2 | $brand-small-icon-size: 30px;
3 |
4 | $gray-1: #e6e6e6;
5 | $gray-2: #a9a9a9;
6 | $gray-3: #797979;
7 | $gray-4: #515151;
8 |
9 | $brand-blue: rgb(67, 135, 253);
10 | $brand-blue-secondary: #3c8ef3;
11 | $brand-blue-secondary2: #2a7cdf;
12 |
13 | $brand-red: rgb(244, 74, 63);
14 | $brand-red-secondary: #e0543e;
15 | $brand-red-secondary2: #d94d3a;
16 |
17 | $brand-yellow: rgb(255, 209, 77);
18 | $brand-yellow-secondary: #f9cc46;
19 | $brand-yellow-secondary2: #f6c000;
20 |
21 | $brand-green: rgb(13, 168, 97);
22 | $brand-green-secondary: #00a86d;
23 | $brand-green-secondary2: #009f5d;
24 |
25 | $slide-width: 900px;
26 | $slide-height: 700px;
27 | $slide-width-widescreen: 1100px;
28 | $slide-top-bottom-padding: 40px;
29 | $slide-left-right-padding: 60px;
30 | $slide-border-radius: 5px;
31 |
32 | $slide-tap-area-width: 100px;
33 |
34 | $article-content-top-padding: 45px;
35 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/theme/scss/hieroglyph.scss:
--------------------------------------------------------------------------------
1 | @import "compass/css3/background-size";
2 |
3 | @import "variables";
4 |
5 | ol {
6 | margin-left: 1.2em;
7 | margin-bottom: 1em;
8 | position: relative;
9 | list-style: decimal;
10 |
11 | li {
12 | margin-bottom: 0.5em;
13 |
14 | ol {
15 | margin-left: 2em;
16 | margin-bottom: 0;
17 | list-style: decimal;
18 |
19 | li:before {
20 | font-weight: 600;
21 | }
22 | }
23 | }
24 |
25 | ol {
26 | margin-top: .5em;
27 | list-style: decimal;
28 |
29 | }
30 | }
31 |
32 | slide.title-image {
33 |
34 | padding-right: 0px;
35 |
36 | hgroup {
37 | position: static !important;
38 |
39 | margin-top: 35%;
40 | padding-left: 30px;
41 |
42 | background: rgba(255, 255, 255, 0.7);
43 |
44 | border-top-left-radius: $slide-border-radius;
45 | -webkit-border-top-left-radius: $slide-border-radius;
46 | -moz-border-top-left-radius: $slide-border-radius;
47 | -o-border-top-left-radius: $slide-border-radius;
48 | }
49 |
50 | hgroup + article {
51 | background: rgba(255, 255, 255, 0.7);
52 |
53 | margin-top: 0px;
54 | padding-left: 30px;
55 |
56 | border-bottom-left-radius: $slide-border-radius;
57 | -webkit-border-bottom-left-radius: $slide-border-radius;
58 | -moz-border-bottom-left-radius: $slide-border-radius;
59 | -o-border-bottom-left-radius: $slide-border-radius;
60 | }
61 |
62 | h1 {
63 | color: #222;
64 | font-size: 3.2em;
65 |
66 | line-height: 1.5em;
67 | font-weight: 500;
68 | }
69 |
70 | div.figure {
71 |
72 | img {
73 | position: absolute;
74 | left: 0;
75 | top: 0;
76 | min-width: 100%;
77 | min-height: 100%;
78 |
79 | border-radius: $slide-border-radius;
80 | -o-border-radius: $slide-border-radius;
81 | -moz-border-radius: $slide-border-radius;
82 | -webkit-border-radius: $slide-border-radius;
83 |
84 | z-index: -1;
85 | }
86 |
87 | .caption {
88 | color: black;
89 | background: rgba(255, 255, 255, 0.25);
90 | padding: 0 5px;
91 | border-bottom-left-radius: $slide-border-radius;
92 | border-top-right-radius: $slide-border-radius;
93 |
94 | position: absolute;
95 | left: 0;
96 | bottom: 0;
97 | margin-bottom: 0;
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/theme/scss/io2013.scss:
--------------------------------------------------------------------------------
1 | @import "compass/css3/background-size";
2 |
3 | @import "variables";
4 |
5 | * {
6 | line-height: 1.3;
7 | }
8 |
9 | h2 {
10 | font-weight: bold;
11 | }
12 | h2, h3 {
13 | color: $gray-4;
14 | }
15 |
16 | q, blockquote {
17 | font-weight: bold;
18 | }
19 |
20 | slides > slide {
21 | color: $gray-4;
22 |
23 | &.title-slide {
24 | &:after {
25 | content: '';
26 | background: url(../../images/io2013/google-io-lockup-1.png) no-repeat 100% 50%;
27 | @include background-size(contain);
28 | position: absolute;
29 | bottom: $slide-top-bottom-padding + 40;
30 | right: $slide-top-bottom-padding;
31 | width: 100%;
32 | height: 90px;
33 | }
34 |
35 | hgroup {
36 | h1 {
37 | font-weight: bold;
38 | line-height: 1.1;
39 | }
40 | h2, p {
41 | color: $gray-4;
42 | }
43 | h2 {
44 | margin-top: 0.25em;
45 | }
46 | p {
47 | margin-top: 3em;
48 | }
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/static/theme/scss/phone.scss:
--------------------------------------------------------------------------------
1 | @import "compass/css3/transition";
2 |
3 |
4 | /*Smartphones (portrait and landscape) ----------- */
5 | /*@media only screen
6 | and (min-width : 320px)
7 | and (max-width : 480px) {
8 |
9 | }*/
10 |
11 | /* Smartphones (portrait) ----------- */
12 | //@media only screen and (max-device-width: 480px) {
13 | /* Styles */
14 | //$slide-width: 350px;
15 | //$slide-height: 500px;
16 |
17 | slides > slide {
18 | /* width: $slide-width !important;
19 | height: $slide-height !important;
20 | margin-left: -$slide-width / 2 !important;
21 | margin-top: -$slide-height / 2 !important;
22 | */
23 | // Don't do full slide transitions on mobile.
24 | -webkit-transition: none !important; // Bug in compass? Not sure why the below is not working
25 | @include transition(none !important);
26 | }
27 |
28 | //}
29 |
30 | /* iPhone 4 ----------- */
31 | @media
32 | only screen and (-webkit-min-device-pixel-ratio : 1.5),
33 | only screen and (min-device-pixel-ratio : 1.5) {
34 | /* Styles */
35 | }
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/theme.conf:
--------------------------------------------------------------------------------
1 | [theme]
2 | inherit = slides2
3 | stylesheet = slides.css
4 |
5 | [options]
6 | custom_css =
7 | custom_js =
8 |
9 | subtitle =
10 | use_builds = true
11 | use_prettify = true
12 | enable_slide_areas = true
13 | enable_touch = true
14 | favicon = ''
15 | presenters =
16 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_slides2/title_slide.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_slides2/title_slide.html
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/layout.html:
--------------------------------------------------------------------------------
1 | {%- extends "basic/layout.html" %}
2 |
3 | {%- block extrahead %}
4 |
5 |
6 |
9 | {% endblock %}
10 |
11 | {% block header %}
12 | {%- if logo %}
13 |
20 | {%- endif %}
21 | {% endblock %}
22 |
23 | {%- block sidebarlogo %}{%- endblock %}
24 | {%- block sidebarsourcelink %}{%- endblock %}
25 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/static/dialog-note.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_theme/static/dialog-note.png
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/static/dialog-seealso.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_theme/static/dialog-seealso.png
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/static/dialog-todo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_theme/static/dialog-todo.png
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/static/dialog-topic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_theme/static/dialog-topic.png
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/static/dialog-warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_theme/static/dialog-warning.png
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/static/footerbg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_theme/static/footerbg.png
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/static/headerbg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_theme/static/headerbg.png
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/static/ie6.css:
--------------------------------------------------------------------------------
1 | * html img,
2 | * html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
3 | this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
4 | this.src = "_static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
5 | this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
6 | this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
7 | );}
8 |
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/static/middlebg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_theme/static/middlebg.png
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/static/transparent.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UWPCE-PythonCert/training.python_web/2e18787539961b9475deddbba507e6686e6dc30b/source/_themes/uwpce_theme/static/transparent.gif
--------------------------------------------------------------------------------
/source/_themes/uwpce_theme/theme.conf:
--------------------------------------------------------------------------------
1 | [theme]
2 | inherit = basic
3 | stylesheet = pyramid.css
4 | pygments_style = sphinx.pygments_styles.PyramidStyle
5 |
--------------------------------------------------------------------------------
/source/presentations/index.rst:
--------------------------------------------------------------------------------
1 | .. slideconf::
2 | :autoslides: False
3 |
4 | Course Presentations
5 | ====================
6 |
7 | .. slide:: Course Presentations
8 | :level: 1
9 |
10 | This document contains no slides.
11 |
12 | Each presentation is the material presented in class for a session of this
13 | course.
14 |
15 | .. toctree::
16 | :maxdepth: 2
17 |
18 | session01
19 | session02
20 | session03
21 | session04
22 | session05
23 | session06
24 | session07
25 | session08
26 | session09
27 | session10
28 |
--------------------------------------------------------------------------------