Skip to content

Commit fe1b744

Browse files
committed
README typo & spacing
2 parents 52e5761 + 8997c2c commit fe1b744

File tree

7 files changed

+160
-24
lines changed

7 files changed

+160
-24
lines changed

README.md

+24-24
Large diffs are not rendered by default.

mod15b-blobstore/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Module 15b - Usage of App Engine `blobstore` with Flask framework in Python 3
2+
3+
This repo folder is the corresponding Python 3 version of the Module 15 app.
4+
5+
- All files in this folder are identical to the _Python 2_ code in the [Module 15 repo folder](/mod15-blobstore) **except**:
6+
1. `app.yaml` was modified for the Python 3 runtime.
7+
1. `appengine_config.py` is unused and thus deleted.
8+
- The _Python 3_ version of the Module 15 app ([Module 15 repo folder](/mod15-blobstore)) features the use of [Blobstore handlers classes](https://cloud.google.com/appengine/docs/standard/python3/services/blobstore).

mod15b-blobstore/app.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
runtime: python310
2+
app_engine_apis: true

mod15b-blobstore/main.py

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Copyright 2022 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import io
16+
from flask import Flask, abort, redirect, request
17+
from google.appengine.api import wrap_wsgi_app
18+
from google.appengine.ext import blobstore, ndb
19+
20+
app = Flask(__name__)
21+
app.wsgi_app = wrap_wsgi_app(app.wsgi_app, use_deferred=True)
22+
23+
24+
class Visit(ndb.Model):
25+
'Visit entity registers visitor IP address & timestamp'
26+
visitor = ndb.StringProperty()
27+
timestamp = ndb.DateTimeProperty(auto_now_add=True)
28+
blob_key = ndb.BlobKeyProperty()
29+
30+
31+
def store_visit(remote_addr, user_agent, upload_key):
32+
'create new Visit entity in Datastore'
33+
Visit(visitor='{}: {}'.format(remote_addr, user_agent),
34+
file_blob = upload_key).put()
35+
36+
37+
def fetch_visits(limit):
38+
'get most recent visits'
39+
return Visit.query().order(-Visit.timestamp).fetch(limit)
40+
41+
42+
class UploadHandler(blobstore.BlobstoreUploadHandler):
43+
'Upload blob (POST) handler'
44+
def post(self):
45+
uploads = self.get_uploads(request.environ)
46+
blob_id = uploads[0].key() if uploads else None
47+
store_visit(self.request.remote_addr, self.request.user_agent, blob_id)
48+
return redirect('/', code=307)
49+
50+
class ViewBlobHandler(blobstore.BlobstoreDownloadHandler):
51+
'view uploaded blob (GET) handler'
52+
def get(self, blob_key):
53+
if not blobstore.get(blob_key):
54+
return "Blobg key not found", 404
55+
else:
56+
headers = self.send_blob(request.environ, blob_key)
57+
58+
# Prevent Flask from setting a default content-type.
59+
# GAE sets it to a guessed type if the header is not set.
60+
headers['Content-Type'] = None
61+
return '', headers
62+
63+
@app.route('/view_photo/<photo_key>')
64+
def view_photo(photo_key):
65+
"""View photo given a key."""
66+
return ViewBlobHandler().get(photo_key)
67+
68+
69+
@app.route('/upload_photo', methods=['POST'])
70+
def upload_photo():
71+
"""Upload handler called by blobstore when a blob is uploaded in the test."""
72+
return UploadHandler().post()
73+
74+
@app.route('/', methods=['GET', 'POST'])
75+
def root():
76+
'main application (GET/POST) handler'
77+
context = {}
78+
if request.method == 'GET':
79+
context['upload_url'] = url_for('upload')
80+
else:
81+
context['visits'] = fetch_visits(10)
82+
return render_template('index.html', **context)

mod15b-blobstore/requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Flask==2.0.1
2+
appengine-python-standard>=1.0.0

mod15b-blobstore/templates/index.html

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>VisitMe Example</title>
5+
</head>
6+
<body>
7+
8+
<h1>VisitMe example</h1>
9+
{% if upload_url %}
10+
11+
<h3>Welcome... upload a file? (optional)</h3>
12+
<form action="{{ upload_url }}" method="POST" enctype="multipart/form-data">
13+
<input type="file" name="file"><p></p>
14+
<input type="submit"> <input type="submit" value="Skip">
15+
</form>
16+
17+
{% else %}
18+
19+
<h3>Last 10 visits</h3>
20+
<ul>
21+
{% for visit in visits %}
22+
<li>{{ visit.timestamp.ctime() }}
23+
<i><code>
24+
{% if visit.file_blob %}
25+
(<a href="/view/{{ visit.file_blob }}" target="_blank">view</a>)
26+
{% else %}
27+
(none)
28+
{% endif %}
29+
</code></i>
30+
from {{ visit.visitor }}
31+
</li>
32+
{% endfor %}
33+
</ul>
34+
35+
{% endif %}
36+
37+
</body>
38+
</html>

mod22-bundled/deferred2/app.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,9 @@ builtins:
2020
- deferred: on
2121

2222
handlers:
23+
- url: /_ah/queue/deferred
24+
script: google.appengine.ext.deferred.deferred.application
25+
login: admin
26+
2327
- url: /.*
2428
script: main.app

0 commit comments

Comments
 (0)