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