Skip to content

Commit

Permalink
Add Python 3 version of Module 15 sample.
Browse files Browse the repository at this point in the history
  • Loading branch information
janetvong authored Mar 23, 2023
1 parent 983f92d commit 8997c2c
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 0 deletions.
8 changes: 8 additions & 0 deletions mod15b-blobstore/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Module 15b - Usage of App Engine `blobstore` with Flask framework in Python 3

This repo folder is the corresponding Python 3 version of the Module 15 app.

- All files in this folder are identical to the _Python 2_ code in the [Module 15 repo folder](/mod15-blobstore) **except**:
1. `app.yaml` was modified for the Python 3 runtime.
1. `appengine_config.py` is unused and thus deleted.
- 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).
2 changes: 2 additions & 0 deletions mod15b-blobstore/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
runtime: python310
app_engine_apis: true
82 changes: 82 additions & 0 deletions mod15b-blobstore/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import io
from flask import Flask, abort, redirect, request
from google.appengine.api import wrap_wsgi_app
from google.appengine.ext import blobstore, ndb

app = Flask(__name__)
app.wsgi_app = wrap_wsgi_app(app.wsgi_app, use_deferred=True)


class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
blob_key = ndb.BlobKeyProperty()


def store_visit(remote_addr, user_agent, upload_key):
'create new Visit entity in Datastore'
Visit(visitor='{}: {}'.format(remote_addr, user_agent),
file_blob = upload_key).put()


def fetch_visits(limit):
'get most recent visits'
return Visit.query().order(-Visit.timestamp).fetch(limit)


class UploadHandler(blobstore.BlobstoreUploadHandler):
'Upload blob (POST) handler'
def post(self):
uploads = self.get_uploads(request.environ)
blob_id = uploads[0].key() if uploads else None
store_visit(self.request.remote_addr, self.request.user_agent, blob_id)
return redirect('/', code=307)

class ViewBlobHandler(blobstore.BlobstoreDownloadHandler):
'view uploaded blob (GET) handler'
def get(self, blob_key):
if not blobstore.get(blob_key):
return "Blobg key not found", 404
else:
headers = self.send_blob(request.environ, blob_key)

# Prevent Flask from setting a default content-type.
# GAE sets it to a guessed type if the header is not set.
headers['Content-Type'] = None
return '', headers

@app.route('/view_photo/<photo_key>')
def view_photo(photo_key):
"""View photo given a key."""
return ViewBlobHandler().get(photo_key)


@app.route('/upload_photo', methods=['POST'])
def upload_photo():
"""Upload handler called by blobstore when a blob is uploaded in the test."""
return UploadHandler().post()

@app.route('/', methods=['GET', 'POST'])
def root():
'main application (GET/POST) handler'
context = {}
if request.method == 'GET':
context['upload_url'] = url_for('upload')
else:
context['visits'] = fetch_visits(10)
return render_template('index.html', **context)
2 changes: 2 additions & 0 deletions mod15b-blobstore/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Flask==2.0.1
appengine-python-standard>=1.0.0
38 changes: 38 additions & 0 deletions mod15b-blobstore/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
</head>
<body>

<h1>VisitMe example</h1>
{% if upload_url %}

<h3>Welcome... upload a file? (optional)</h3>
<form action="{{ upload_url }}" method="POST" enctype="multipart/form-data">
<input type="file" name="file"><p></p>
<input type="submit"> <input type="submit" value="Skip">
</form>

{% else %}

<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
<li>{{ visit.timestamp.ctime() }}
<i><code>
{% if visit.file_blob %}
(<a href="/view/{{ visit.file_blob }}" target="_blank">view</a>)
{% else %}
(none)
{% endif %}
</code></i>
from {{ visit.visitor }}
</li>
{% endfor %}
</ul>

{% endif %}

</body>
</html>

0 comments on commit 8997c2c

Please sign in to comment.