├── .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 |
  1. CGI Test 1
  2. 12 |
  3. Exercise One
  4. 13 |
  5. CGI Sum Server
  6. 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 |
13 | 18 |
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 |
5 | {% for field in form %} 6 | {% if field.errors %} 7 |
    8 | {% for error in field.errors %} 9 |
  • {{ error }}
  • 10 | {% endfor %} 11 |
12 | {% endif %} 13 |

{{ field.label }}: {{ field }}

14 | {% endfor %} 15 |

16 |
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 |
13 | 18 |
19 |
20 |

My Python Journal

21 |
22 | {% block body %}{% endblock %} 23 |
24 |
25 |
26 |

Created in the UW PCE Python Certificate Program

27 |
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 |
    6 | {% for entry in entries %} 7 |
  • 8 | {{ entry.title }} 9 |
  • 10 | {% endfor %} 11 |
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 |
5 | {% for field in form %} 6 | {% if field.errors %} 7 |
    8 | {% for error in field.errors %} 9 |
  • {{ error }}
  • 10 | {% endfor %} 11 |
12 | {% endif %} 13 |

{{ field.label }}: {{ field }}

14 | {% endfor %} 15 |

16 |
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 |
13 | 18 |
19 |
20 |

My Python Journal

21 |
22 | {% block body %}{% endblock %} 23 |
24 |
25 |
26 |

Created in the UW PCE Python Certificate Program

27 |
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 | 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 | 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 |

10 | {{ post }} 11 |

12 | 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 |
{% csrf_token %} 6 | {{ form.as_p }} 7 |

8 |
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 | 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 |

10 | {{ post }} 11 |

12 | 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 |
{% csrf_token %} 6 | {{ form.as_p }} 7 |

8 |
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 | ![Mobile vs desktop users](image.png) 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 | ![Many kinds of devices.](image.png) 31 | 32 |
          source: place source info here
          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 |
          14 | 19 |
          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 | --------------------------------------------------------------------------------