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