import gdrive.py and the ajaxuploader app
[oweals/karmaworld.git] / karmaworld / apps / notes / gdrive.py
1 #!/usr/bin/env python
2 # -*- coding:utf8 -*-
3 # Copyright (C) 2012  FinalsClub Foundation
4
5 import datetime
6 import mimetypes
7 import os
8
9 import httplib2
10 from apiclient.discovery import build
11 from apiclient.http import MediaFileUpload
12 from oauth2client.client import flow_from_clientsecrets
13
14 from kamraworld.apps.notes.models import DriveAuth, Note
15
16 CLIENT_SECRET = './notes/client_secrets.json' # FIXME
17 from credentials import GOOGLE_USER # FIXME
18 EXT_TO_MIME = {'.docx': 'application/msword'}
19
20 def build_flow():
21     """ Create an oauth2 autentication object with our preferred details """
22     scopes = [
23         'https://www.googleapis.com/auth/drive',
24         'https://www.googleapis.com/auth/drive.file',
25         'https://www.googleapis.com/auth/userinfo.email',
26         'https://www.googleapis.com/auth/userinfo.profile',
27     ]
28
29     flow = flow_from_clientsecrets(CLIENT_SECRET, ' '.join(scopes), \
30             redirect_uri='http://localhost:8000/oauth2callback')
31     flow.params['access_type'] = 'offline'
32     flow.params['approval_prompt'] = 'force'
33     flow.params['user_id'] = GOOGLE_USER
34     return flow
35
36
37 def authorize():
38     """ Use an oauth2client flow object to generate the web url to create a new
39         auth that can be then stored """
40     flow = build_flow()
41     print flow.step1_get_authorize_url()
42
43
44 def accept_auth(code):
45     """ Callback endpoint for accepting the post `authorize()` google drive 
46         response, and generate a credentials object
47         :code:  An authentication token from a WEB oauth dialog
48         returns a oauth2client credentials object """
49     flow = build_flow()
50     creds = flow.step2_exchange(code)
51     return creds
52
53
54 def build_api_service(creds):
55     http = httplib2.Http()
56     http = creds.authorize(http)
57     return build('drive', 'v2', http=http), http
58
59
60 def check_and_refresh(creds, auth):
61     """ Check a Credentials object's expiration token
62         if it is out of date, refresh the token and save
63         :creds: a Credentials object
64         :auth:  a DriveAuth that backs the cred object
65         :returns: updated creds and auth objects
66     """
67     if creds.token_expiry < datetime.datetime.utcnow():
68         # if we are passed the token expiry, 
69         # refresh the creds and store them
70         http = httplib2.Http()
71         http = creds.authorize(http)
72         creds.refresh(http)
73         auth.credentials = creds.to_json()
74         auth.save()
75     return creds, auth
76
77
78 def convert_with_google_drive(note):
79     """ Upload a local note and download HTML
80         using Google Drive
81         :note: a File model instance # FIXME
82     """
83     # Get file_type and encoding of uploaded file
84     # i.e: file_type = 'text/plain', encoding = None
85     (file_type, encoding) = mimetypes.guess_type(note.file.path)
86
87     # If mimetype cannot be guessed
88     # Check against known issues, then
89     # finally, Raise Exception
90     # Extract file extension and compare it to EXT_TO_MIME dict
91
92     fileName, fileExtension = os.path.splitext(note.file.path)
93
94     if file_type == None:
95
96         if fileExtension.strip().lower() in EXT_TO_MIME:
97             file_type = EXT_TO_MIME[fileExtension.strip().lower()]
98         # If boy mimetypes.guess_type and EXT_TO_MIME fail to cover
99         # file, return error
100         else:
101             raise Exception('Unknown file type')
102
103     resource = {
104                 'title':    note.title,
105                 'desc':     note.description,
106                 'mimeType': file_type
107             }
108     # TODO: set the permission of the file to permissive so we can use the 
109     #       gdrive_url to serve files directly to users
110     media = MediaFileUpload(note.file.path, mimetype=file_type,
111                 chunksize=1024*1024, resumable=True)
112
113     auth = DriveAuth.objects.filter(email=GOOGLE_USER).all()[0]
114     creds = auth.transform_to_cred()
115
116
117     creds, auth = check_and_refresh(creds, auth)
118
119     service, http = build_api_service(creds)
120
121     # Upload the file
122     # TODO: wrap this in a try loop that does a token refresh if it fails
123     print "Trying to upload document"
124     file_dict = service.files().insert(body=resource, media_body=media, convert=True).execute()
125
126     # set note.is_pdf
127     if file_type == 'application/pdf':
128         # Get a new copy of the file from the database with the new metadata from filemeta
129         new_file = File.objects.get(id=note.id)
130         # If it's a pdf, instead save an embed_url from resource['selfLink']
131         new_file.is_pdf = True
132         new_file.embed_url = file_dict[u'selfLink']
133         new_file.gdrive_url = file_dict[u'downloadUrl']
134     else:
135         # get the converted filetype urls
136         download_urls = {}
137         download_urls['html'] = file_dict[u'exportLinks']['text/html']
138         download_urls['text'] = file_dict[u'exportLinks']['text/plain']
139         content_dict = {}
140
141
142         for download_type, download_url in download_urls.items():
143             print "\n%s -- %s" % (download_type, download_urls)
144             resp, content = http.request(download_url, "GET")
145
146
147             if resp.status in [200]:
148                 print "\t downloaded!"
149                 # save to the File.property resulting field
150                 content_dict[download_type] = content
151             else:
152                 print "\t Download failed: %s" % resp.status
153
154         # Get a new copy of the file from the database with the new metadata from filemeta
155         new_file = Note.objects.get(id=note.id)
156
157         # set the .odt as the download from google link
158         new_file.gdrive_url = file_dict[u'exportLinks']['application/vnd.oasis.opendocument.text']
159         new_file.html = content_dict['html']
160         new_file.text = content_dict['text']
161
162     # Finally, save whatever data we got back from google
163     new_file.save()