7
from google.appengine.api import users
8
from google.appengine.api import mail
10
from google.appengine.ext import webapp
11
from google.appengine.ext.webapp import template
12
from google.appengine.ext.webapp.util import run_wsgi_app
13
from google.appengine.ext import db
16
Version 2: Using partition to store larger model files to the online db
18
# Changes to be deployment:
19
* Add admin permissions back to /delete domain
20
* Design a sleeker webform layout
24
class PartitionObj(db.Model):
25
model = db.StringProperty(multiline=False)
26
owner = db.StringProperty(multiline=True)
27
name = db.StringProperty(multiline=True)
28
content = db.BlobProperty()
30
class ColladaModel(db.Model):
31
name = db.StringProperty(multiline=False)
32
author = db.StringProperty(multiline=True)
33
fiducial = db.StringProperty(multiline=True)
34
model = db.BlobProperty()
36
texture = db.BlobProperty()
37
album = db.StringProperty(multiline=True)
39
class Fiducial(db.Model):
40
name = db.StringProperty(multiline=False)
41
marker = db.BlobProperty()
42
pic = db.BlobProperty()
43
used = db.BooleanProperty()
44
model = db.StringProperty(multiline=False)
46
class Album(db.Model):
47
name = db.StringProperty(multiline=False)
48
models = db.StringProperty(multiline=True)
49
desc = db.StringProperty(multiline=True)
50
broadcast = db.BooleanProperty()
53
def __init__(self, obj, name, owner, psize=500):
59
self.psize = psize*1024
61
# The file object is a byte array, so we can partition into
62
# chunks of 500 kb, and return a link to first obj
65
next_byte = len(self.file)
66
current_byte = len(self.file)
67
while (current_byte > self.psize):
68
next_byte = current_byte - self.psize
69
pobj = PartitionObj(model=self.model, \
70
owner=self.owner, name=str(partition_num), \
71
content=self.file[next_byte:current_byte])
74
current_byte = next_byte
76
retObj = PartitionObj(model=self.model, \
77
owner=self.owner, name=str(partition_num + 1), \
78
content=self.file[0:next_byte])
81
def combinePartitions(name, owner):
82
partitions = db.GqlQuery("SELECT * FROM PartitionObj WHERE model='%s' \
83
AND owner='%s' ORDER BY name DESC" % (name, owner))
84
partitions = partitions.fetch(partitions.count())
88
for partition in partitions:
89
for byte in partition.content:
94
class MainPage(webapp.RequestHandler):
96
path = os.path.join(os.path.dirname(__file__), 'index.html')
99
albums = db.GqlQuery("SELECT * FROM Album")
100
albums = albums.fetch(albums.count())
101
context['models'] = []
104
context['albums'] = albums
106
if album.models != None:
107
models = db.GqlQuery("SELECT * FROM ColladaModel WHERE album = '%s'" % album.name)
108
models = models.fetch(models.count())
109
retobj = ModelTable()
110
retobj.name = album.name
111
retobj.content = models
112
context['models'].append(retobj)
114
self.response.out.write(template.render(path, context))
121
class UploadModel(webapp.RequestHandler):
126
# flag to write to db
130
collada = ColladaModel()
132
# Need to enforce email
133
collada.author = self.request.get('author')
134
if len(collada.author) < 7 or re.match("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$", collada.author) == None:
135
self.dbg += "Please enter a properly formatted email. "
137
collada.name= self.request.get('name')
138
# If name is blank, use email
139
if collada.name == "":
140
self.dbg += "Please enter a model name. "
143
# Get model and compress before upload
144
model = self.request.get('model')
145
# Check if model not null, should add xml validation at some point
146
# Need to learn more about libraries available on app engine
148
self.dbg += "Please select a collada model. "
150
cmodel = zlib.compress(model)
152
# Check if album is selected
153
album = self.request.get('albums')
155
self.dbg += "Please create an album. "
157
album = db.GqlQuery("SELECT * FROM Album WHERE name = '%s'" % album)
158
if album.count() == 0:
159
self.dbg += "Album has been deleted. Please create another album."
161
if len(self.dbg) > 0:
164
# Sanity Check: At this point, all values are stored somehow
166
# Get and assign fiducial marker
167
fiducials = db.GqlQuery("SELECT * FROM Fiducial WHERE used = False")
168
if fiducials.count() > 0 and self.process:
169
self.dbg = "Model successfully written to the database. Please \
170
check your email for the ARTag!"
171
marker = (fiducials.fetch(1))[0]
173
# Currently stores string, fix later
174
collada.fiducial = marker.name
175
marker.model = collada.name
177
# Handle model attachment
178
if len(cmodel)/1024 > 500:
179
partition = Partition(cmodel, collada.name, collada.author)
180
partition.partition()
182
collada.model = cmodel
184
# Handles texture attachment, if application
185
textures = self.request.get("texture")
187
if len(textures)/1024 > 500:
188
partition = Partition(textures, collada.name + "_texture", collada.author)
189
partition.partition()
190
collada.texture = "PARTITION"
191
elif len(textures)/1024 > 0:
192
collada.texture = textures
195
album = album.fetch(1)[0]
196
collada.album = album.name
198
album.models += " " + collada.name
200
album.models = collada.name
202
# Send an email with an attachment
204
(sender="jnoraky@gmail.com",
208
Attached is your ARTag. Print it and bring it to your destination.
209
Your unique ARTag identifier is <b>%s</b>. This will be used to load your
214
attachments=[(marker.name, marker.pic)]
217
# Disable ARTag for other models
224
elif fiducials.count() <= 0 and self.process:
225
self.dbg = "Sorry, your model could not be uploaded. Please \
226
try again when more ARTags become available"
227
self.redirect('/?tab=step2&dbg=%s' % self.dbg)
230
def redirect(self, uri, permanent=False):
231
self.response.out.write(
234
<script language="javascript" type="text/javascript">
236
window.location="%s";
239
""" % (self.dbg, uri))
245
# For use only in development. In actual instantiation, all
246
# fiducial markers will be loaded beforehand.
247
class FiducialUploader(webapp.RequestHandler):
249
self.response.out.write("""
251
<head><title>Fiducial Uploader</title></head>
253
<form action="/fiducialupload" enctype="multipart/form-data" method="POST">
254
Please enter the marker name:<br>
255
<input name="marker" type="text" size="30">
257
Please upload the image of the fiducial:<br>
258
<input name="pic" type="file">
260
Please upload the pattern file of the fiducial:<br>
261
<input name="fiducial" type="file">
263
<input type="submit" value="Submit">
269
class UploadFiducial(webapp.RequestHandler):
271
fiducial = Fiducial()
272
fiducial.name = self.request.get('marker')
273
fiducial.pic = db.Blob(self.request.get('pic'))
274
fiducial.marker = db.Blob(self.request.get('fiducial'))
275
fiducial.used = False
277
self.redirect('/fiducial')
279
class CreateAlbum(webapp.RequestHandler):
282
name = self.request.get('name')
284
album.desc = self.request.get('description')
285
# Check if there is a name and eventually check if other names
289
dbg = 'Awesome! Album %s successfully created!' % name
292
dbg = 'Please enter a name'
293
self.redirect('/?tab=step1&dbg=%s' % dbg)
295
# Download the collada file
296
class DownloadModel(webapp.RequestHandler):
297
def get(self, resource):
298
model_name = str(urllib.unquote(resource))
299
models = db.GqlQuery("SELECT * FROM ColladaModel WHERE name= '%s'" % model_name)
300
if models.count() > 0:
301
model = (models.fetch(1))[0]
302
if model.model == None:
303
mfiles = combinePartitions(model.name, model.author)
304
self.response.out.write(zlib.decompress(mfiles))
306
self.response.out.write(zlib.decompress(model.model))
308
# Download the pattern file
309
class DownloadPattern(webapp.RequestHandler):
310
def get(self, resource):
311
model_name = str(urllib.unquote(resource))
312
patterns = db.GqlQuery("SELECT * FROM Fiducial WHERE model= '%s'" % model_name)
313
if patterns.count() > 0:
314
pattern = (patterns.fetch(1))[0]
315
self.response.out.write(pattern.marker)
317
# Download the textures
318
class DownloadTexture(webapp.RequestHandler):
319
def get(self, resource):
320
texture_name = str(urllib.unquote(resource))
321
textures = db.GqlQuery("SELECT * FROM ColladaModel WHERE name = '%s'" % texture_name)
322
if textures.count() > 0:
323
texture = (textures.fetch(1))[0]
324
if texture.texture == "PARTITION":
325
combined_texture = combinePartitions(texture.name + "_texture", texture.author)
326
self.response.out.write(combined_texture)
327
elif texture.texture != None:
328
self.response.headers['Content-Type'] = "application/zip"
329
self.response.out.write(texture.texture)
331
# Clear db of models and reset the fiducial markers, run at midnight
332
class clearDb(webapp.RequestHandler):
335
models = db.GqlQuery("SELECT * FROM ColladaModel")
336
models = models.fetch(models.count())
340
partitions = db.GqlQuery("SELECT * FROM PartitionObj")
341
partitions = partitions.fetch(partitions.count())
342
db.delete(partitions)
345
tags = db.GqlQuery("SELECT * FROM Fiducial")
346
tags = tags.fetch(tags.count())
351
self.response.out.write("All Models have been deleted")
353
class All(webapp.RequestHandler):
355
albums = db.GqlQuery("SELECT * FROM Album WHERE broadcast=True")
357
album = albums.fetch(1)[0]
358
self.response.out.write(album.models)
360
self.response.out.write("")
362
class Broadcast(webapp.RequestHandler):
364
dbg = 'Album %s is now successfully being broadcasted!' % self.request.get('albums')
365
albums = db.GqlQuery("SELECT * FROM Album")
366
albums = albums.fetch(albums.count())
368
if album.name == self.request.get('albums'):
369
album.broadcast = True
371
album.broadcast = False
374
self.redirect('/?tab=step3&dbg=%s' % dbg)
376
application = webapp.WSGIApplication(
378
('/upload', UploadModel),
379
('/createAlbum', CreateAlbum),
380
('/broadcast', Broadcast),
381
('/fiducial', FiducialUploader),
382
('/fiducialupload', UploadFiducial),
384
('/download/([^/]+)?', DownloadModel),
385
('/pattern/([^/]+)?', DownloadPattern),
386
('/texture/([^/]+)?', DownloadTexture),
387
('/delete', clearDb)],
391
run_wsgi_app(application)
393
if __name__ == "__main__":