~nchohan/+junk/mytools

« back to all changes in this revision

Viewing changes to sample_apps/ModelUploader/ModelUploader.py

  • Committer: root
  • Date: 2010-11-03 07:43:57 UTC
  • Revision ID: root@appscale-image0-20101103074357-xea7ja3sor3x93oc
init

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import cgi
 
2
import urllib
 
3
import zlib
 
4
import re
 
5
import os
 
6
 
 
7
from google.appengine.api import users
 
8
from google.appengine.api import mail
 
9
 
 
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
 
14
 
 
15
'''
 
16
Version 2: Using partition to store larger model files to the online db
 
17
 
 
18
# Changes to be deployment:
 
19
* Add admin permissions back to /delete domain
 
20
* Design a sleeker webform layout
 
21
* Organize CSS
 
22
'''
 
23
 
 
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()
 
29
 
 
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()
 
35
    # Stores ZipFile obj
 
36
    texture = db.BlobProperty()
 
37
    album = db.StringProperty(multiline=True)
 
38
    
 
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)
 
45
 
 
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()
 
51
    
 
52
class Partition:
 
53
    def __init__(self, obj, name, owner, psize=500):
 
54
        self.file = obj
 
55
        # Use to ID
 
56
        self.model = name
 
57
        self.owner = owner
 
58
        # Partition size
 
59
        self.psize = psize*1024
 
60
        
 
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
 
63
    def partition(self):
 
64
        partition_num = 0
 
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])
 
72
            pobj.put()
 
73
            partition_num += 1
 
74
            current_byte = next_byte
 
75
            
 
76
        retObj = PartitionObj(model=self.model, \
 
77
                            owner=self.owner, name=str(partition_num + 1), \
 
78
                            content=self.file[0:next_byte])
 
79
        retObj.put()
 
80
        
 
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())
 
85
 
 
86
    
 
87
    merged = ""
 
88
    for partition in partitions:
 
89
        for byte in partition.content:
 
90
            merged += byte
 
91
        
 
92
    return merged
 
93
 
 
94
class MainPage(webapp.RequestHandler):
 
95
    def get(self):
 
96
        path = os.path.join(os.path.dirname(__file__), 'index.html')
 
97
        context = {}
 
98
 
 
99
        albums = db.GqlQuery("SELECT * FROM Album")
 
100
        albums = albums.fetch(albums.count())
 
101
        context['models'] = []
 
102
        
 
103
        if len(albums) > 0:
 
104
            context['albums'] = albums 
 
105
            for album in 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)
 
113
            
 
114
        self.response.out.write(template.render(path, context))
 
115
 
 
116
class ModelTable:
 
117
    name = ""
 
118
    content = []
 
119
    
 
120
 
 
121
class UploadModel(webapp.RequestHandler):
 
122
 
 
123
    # dbg message
 
124
    dbg = ""
 
125
 
 
126
    # flag to write to db
 
127
    process = True
 
128
    
 
129
    def post(self):
 
130
        collada = ColladaModel()
 
131
        
 
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. "
 
136
 
 
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. "
 
141
            
 
142
        
 
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
 
147
        if model == "":
 
148
            self.dbg += "Please select a collada model. "
 
149
        else:
 
150
            cmodel = zlib.compress(model)
 
151
 
 
152
        # Check if album is selected
 
153
        album = self.request.get('albums')
 
154
        if album == None:
 
155
            self.dbg += "Please create an album. "            
 
156
        else:
 
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."
 
160
        
 
161
        if len(self.dbg) > 0:
 
162
            self.process = False
 
163
            
 
164
        # Sanity Check: At this point, all values are stored somehow
 
165
        
 
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]
 
172
 
 
173
            # Currently stores string, fix later
 
174
            collada.fiducial = marker.name
 
175
            marker.model = collada.name 
 
176
 
 
177
            # Handle model attachment
 
178
            if len(cmodel)/1024 > 500:
 
179
                partition = Partition(cmodel, collada.name, collada.author)
 
180
                partition.partition()
 
181
            else:
 
182
                collada.model = cmodel
 
183
 
 
184
            # Handles texture attachment, if application
 
185
            textures = self.request.get("texture")
 
186
            if textures != None:
 
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
 
193
 
 
194
            # Handles album
 
195
            album = album.fetch(1)[0]
 
196
            collada.album = album.name
 
197
            try:
 
198
                album.models += " " + collada.name
 
199
            except:
 
200
                album.models = collada.name
 
201
             
 
202
            # Send an email with an attachment
 
203
            mail.send_mail \
 
204
                (sender="jnoraky@gmail.com",
 
205
                 to = collada.author,
 
206
                 subject = "ARTag",
 
207
                 body = """
 
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
 
210
model on the client.
 
211
 
 
212
The Ops Lab
 
213
                 """ % collada.name,
 
214
                 attachments=[(marker.name, marker.pic)]
 
215
                 )
 
216
 
 
217
            # Disable ARTag for other models
 
218
            marker.used = True
 
219
            
 
220
            # Push to server
 
221
            db.put(marker)
 
222
            db.put(collada)
 
223
            db.put(album)
 
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)
 
228
 
 
229
    '''
 
230
    def redirect(self, uri, permanent=False):
 
231
        self.response.out.write(
 
232
                """
 
233
                <html><head>
 
234
                <script language="javascript" type="text/javascript">
 
235
                alert("%s");
 
236
                window.location="%s";
 
237
                </script>
 
238
                </head></html>
 
239
                """ % (self.dbg, uri))
 
240
    '''
 
241
     
 
242
    
 
243
 
 
244
 
 
245
# For use only in development. In actual instantiation, all
 
246
# fiducial markers will be loaded beforehand.
 
247
class FiducialUploader(webapp.RequestHandler):
 
248
    def get(self):
 
249
        self.response.out.write("""
 
250
            <html> 
 
251
            <head><title>Fiducial Uploader</title></head> 
 
252
            <body> 
 
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"> 
 
256
                    <br><br> 
 
257
                    Please upload the image of the fiducial:<br> 
 
258
                    <input name="pic" type="file"> 
 
259
                    <br><br>
 
260
                    Please upload the pattern file of the fiducial:<br>
 
261
                    <input name="fiducial" type="file">
 
262
                    <br><br>
 
263
                    <input type="submit" value="Submit"> 
 
264
                </form> 
 
265
            </body>
 
266
            </html>
 
267
        """)
 
268
 
 
269
class UploadFiducial(webapp.RequestHandler):
 
270
    def post(self):
 
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
 
276
        fiducial.put()
 
277
        self.redirect('/fiducial')
 
278
 
 
279
class CreateAlbum(webapp.RequestHandler):
 
280
    def post(self):
 
281
        album = Album()
 
282
        name = self.request.get('name')
 
283
        dbg = ''
 
284
        album.desc = self.request.get('description')
 
285
        # Check if there is a name and eventually check if other names
 
286
        # exist
 
287
        if len(name) > 0:
 
288
            album.name = name
 
289
            dbg = 'Awesome! Album %s successfully created!' % name
 
290
            album.put()
 
291
        else:
 
292
            dbg = 'Please enter a name'
 
293
        self.redirect('/?tab=step1&dbg=%s' % dbg)
 
294
        
 
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))
 
305
            else:
 
306
                self.response.out.write(zlib.decompress(model.model))
 
307
 
 
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)
 
316
 
 
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)
 
330
            
 
331
# Clear db of models and reset the fiducial markers, run at midnight
 
332
class clearDb(webapp.RequestHandler):
 
333
    def get(self):
 
334
        # delete models
 
335
        models = db.GqlQuery("SELECT * FROM ColladaModel")
 
336
        models = models.fetch(models.count())
 
337
        db.delete(models)
 
338
 
 
339
        # delete partitions
 
340
        partitions = db.GqlQuery("SELECT * FROM PartitionObj")
 
341
        partitions = partitions.fetch(partitions.count())
 
342
        db.delete(partitions)
 
343
        
 
344
        #reset fiducials
 
345
        tags = db.GqlQuery("SELECT * FROM Fiducial")
 
346
        tags = tags.fetch(tags.count())
 
347
        for tag in tags:
 
348
            tag.used = False
 
349
            tag.put()
 
350
            
 
351
        self.response.out.write("All Models have been deleted")
 
352
 
 
353
class All(webapp.RequestHandler):
 
354
    def get(self):
 
355
        albums = db.GqlQuery("SELECT * FROM Album WHERE broadcast=True")
 
356
        try:
 
357
            album = albums.fetch(1)[0]
 
358
            self.response.out.write(album.models)
 
359
        except:
 
360
            self.response.out.write("")
 
361
 
 
362
class Broadcast(webapp.RequestHandler):
 
363
    def post(self):
 
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())
 
367
        for album in albums:
 
368
            if album.name == self.request.get('albums'):
 
369
                album.broadcast = True
 
370
            else:
 
371
                album.broadcast = False
 
372
            db.put(album)
 
373
        
 
374
        self.redirect('/?tab=step3&dbg=%s' % dbg)
 
375
        
 
376
application = webapp.WSGIApplication(
 
377
                                    [('/', MainPage), 
 
378
                                     ('/upload', UploadModel),
 
379
                                     ('/createAlbum', CreateAlbum),
 
380
                                     ('/broadcast', Broadcast),
 
381
                                     ('/fiducial', FiducialUploader),        
 
382
                                     ('/fiducialupload', UploadFiducial),
 
383
                                     ('/all', All),
 
384
                                     ('/download/([^/]+)?', DownloadModel),
 
385
                                     ('/pattern/([^/]+)?', DownloadPattern),
 
386
                                     ('/texture/([^/]+)?', DownloadTexture),
 
387
                                     ('/delete', clearDb)],
 
388
                                    debug=True)
 
389
 
 
390
def main():
 
391
    run_wsgi_app(application)
 
392
 
 
393
if __name__ == "__main__":
 
394
    main()