Merge branch 'ppt-gdrive' of https://github.com/FinalsClub/karmaworld into ppt-gdrive
[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 from io import FileIO, BufferedWriter
7 import mimetypes
8 import os
9 import time
10
11 import httplib2
12 from apiclient.discovery import build
13 from apiclient.http import MediaFileUpload
14 from django.conf import settings
15 from django.core.files import File
16 from oauth2client.client import flow_from_clientsecrets
17
18 from karmaworld.apps.notes.models import DriveAuth, Note
19
20 CLIENT_SECRET = os.path.join(settings.DJANGO_ROOT, \
21                     'secret/client_secrets.json')
22 #from credentials import GOOGLE_USER # FIXME
23 try:
24     from secrets.drive import GOOGLE_USER
25 except:
26     GOOGLE_USER = 'admin@karmanotes.org' # FIXME
27
28 EXT_TO_MIME = {'.docx': 'application/msword'}
29
30 def build_flow():
31     """ Create an oauth2 autentication object with our preferred details """
32     scopes = [
33         'https://www.googleapis.com/auth/drive',
34         'https://www.googleapis.com/auth/drive.file',
35         'https://www.googleapis.com/auth/userinfo.email',
36         'https://www.googleapis.com/auth/userinfo.profile',
37     ]
38
39     flow = flow_from_clientsecrets(CLIENT_SECRET, ' '.join(scopes), \
40             redirect_uri='http://localhost:8000/oauth2callback')
41     flow.params['access_type'] = 'offline'
42     flow.params['approval_prompt'] = 'force'
43     flow.params['user_id'] = GOOGLE_USER
44     return flow
45
46
47 def authorize():
48     """ Use an oauth2client flow object to generate the web url to create a new
49         auth that can be then stored """
50     flow = build_flow()
51     print flow.step1_get_authorize_url()
52
53
54 def accept_auth(code):
55     """ Callback endpoint for accepting the post `authorize()` google drive
56         response, and generate a credentials object
57         :code:  An authentication token from a WEB oauth dialog
58         returns a oauth2client credentials object """
59     flow = build_flow()
60     creds = flow.step2_exchange(code)
61     return creds
62
63
64 def build_api_service(creds):
65     http = httplib2.Http()
66     http = creds.authorize(http)
67     return build('drive', 'v2', http=http), http
68
69
70 def check_and_refresh(creds, auth):
71     """ Check a Credentials object's expiration token
72         if it is out of date, refresh the token and save
73         :creds: a Credentials object
74         :auth:  a DriveAuth that backs the cred object
75         :returns: updated creds and auth objects
76     """
77     if creds.token_expiry < datetime.datetime.utcnow():
78         # if we are passed the token expiry,
79         # refresh the creds and store them
80         http = httplib2.Http()
81         http = creds.authorize(http)
82         creds.refresh(http)
83         auth.credentials = creds.to_json()
84         auth.save()
85     return creds, auth
86
87 def download_from_gdrive(file_dict, http, extension):
88     """ get urls from file_dict and download contextual files from google """
89     download_urls = {}
90     download_urls['text'] = file_dict[u'exportLinks']['text/plain']
91     if extension.lower() in ['.ppt', 'pptx']:
92         download_urls['pdf'] = file_dict[u'exportLinks']['application/pdf']
93     else:
94         download_urls['html'] = file_dict[u'exportLinks']['text/html']
95
96
97     content_dict = {}
98     for download_type, download_url in download_urls.items():
99         print "\n%s -- %s" % (download_type, download_urls)
100         resp, content = http.request(download_url, "GET")
101
102         if resp.status in [200]:
103             print "\t downloaded!"
104             # save to the File.property resulting field
105             content_dict[download_type] = content
106         else:
107             print "\t Download failed: %s" % resp.status
108
109     return content_dict
110
111 def upload_to_gdrive(service, media, filename, extension):
112     """ take a gdrive service object, and a media wrapper and upload to gdrive
113         returns a file_dict """
114     _resource = {'title': filename}
115     if extension.lower() in ['.pdf', '.jpeg', '.jpg', '.png']:
116         # include OCR on ocr-able files
117         file_dict = service.files().insert(body=_resource, media_body=media, convert=True, ocr=True).execute()
118
119     else:
120         file_dict = service.files().insert(body=_resource, media_body=media, convert=True).execute()
121
122     if u'exportLinks' not in file_dict:
123         # wait some seconds
124         # get the doc from gdrive
125         time.sleep(30)
126         file_dict = service.files().get(fileId=file_dict[u'id']).execute()
127
128     return file_dict
129
130 def convert_with_google_drive(note):
131     """ Upload a local note and download HTML
132         using Google Drive
133         :note: a File model instance # FIXME
134     """
135     # TODO: set the permission of the file to permissive so we can use the
136     #       gdrive_url to serve files directly to users
137
138     # Get file_type and encoding of uploaded file
139     # i.e: file_type = 'text/plain', encoding = None
140     (file_type, encoding) = mimetypes.guess_type(note.note_file.path)
141
142
143
144     if file_type != None:
145         media = MediaFileUpload(note.note_file.path, mimetype=file_type,
146                     chunksize=1024*1024, resumable=True)
147
148     else:
149         media = MediaFileUpload(note.note_file.path,
150                     chunksize=1024*1024, resumable=True)
151
152     auth = DriveAuth.objects.filter(email=GOOGLE_USER).all()[0]
153     creds = auth.transform_to_cred()
154
155
156     creds, auth = check_and_refresh(creds, auth)
157
158     service, http = build_api_service(creds)
159
160     # get the file extension
161     filename, extension = os.path.splitext(note.note_file.path)
162
163     file_dict = upload_to_gdrive(service, media, filename, extension)
164
165     content_dict = download_from_gdrive(file_dict, http, extension)
166
167
168     # Get a new copy of the file from the database with the new metadata from filemeta
169     new_note = Note.objects.get(id=note.id)
170     if extension.lower() == '.pdf':
171         new_note.file_type = 'pdf'
172
173     elif extension.lower() in ['.ppt', '.pptx']:
174         print "try to save ppt"
175         now = datetime.datetime.utcnow()
176         # create a folder path to store the ppt > pdf file with year and month folders
177         _path = os.path.join(settings.MEDIA_ROOT, 'ppt_pdf/%s/%s' % (now.year, now.month), filename)
178         try:
179             # If those folders don't exist, create them
180             os.makedirs(os.path.realpath(os.path.dirname(_path)))
181
182         _writer = BufferedWriter(FileIO(_path, "w"))
183         _writer.write(content_dict['pdf'])
184         _writer.close()
185
186
187         new_note.pdf_file = os.path.join(_path, filename)
188
189     # set the .odt as the download from google link
190     if extension.lower() in ['.ppt', '.pptx']:
191         print "is ppt"
192         new_note.pdf_file = File(content_dict['pdf'])
193     else:
194         # PPT files do not have this export ability
195         new_note.gdrive_url = file_dict[u'exportLinks']['application/vnd.oasis.opendocument.text']
196         new_note.html = content_dict['html']
197
198     new_note.text = content_dict['text']
199
200     # before we save new html, sanitize a tags in note.html
201     new_note.sanitize_html(save=False)
202
203     # Finally, save whatever data we got back from google
204     new_note.save()