api2.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. #!/usr/bin/env python
  2. # Licensed to Cloudera, Inc. under one
  3. # or more contributor license agreements. See the NOTICE file
  4. # distributed with this work for additional information
  5. # regarding copyright ownership. Cloudera, Inc. licenses this file
  6. # to you under the Apache License, Version 2.0 (the
  7. # "License"); you may not use this file except in compliance
  8. # with the License. You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. import logging
  18. import json
  19. import tempfile
  20. import time
  21. import StringIO
  22. import zipfile
  23. from django.contrib.auth.models import Group, User
  24. from django.core import management
  25. from django.http import HttpResponse
  26. from django.shortcuts import redirect
  27. from django.utils import html
  28. from django.views.decorators.http import require_POST
  29. from desktop.lib.django_util import JsonResponse
  30. from desktop.lib.export_csvxls import make_response
  31. from desktop.lib.i18n import smart_str, force_unicode
  32. from desktop.models import Document2, Document, Directory
  33. from desktop.lib.exceptions_renderable import PopupException
  34. from hadoop.fs.hadoopfs import Hdfs
  35. LOG = logging.getLogger(__name__)
  36. def api_error_handler(func):
  37. def decorator(*args, **kwargs):
  38. response = {}
  39. try:
  40. return func(*args, **kwargs)
  41. except Exception, e:
  42. LOG.exception('Error running %s' % func)
  43. response['status'] = -1
  44. response['message'] = force_unicode(str(e))
  45. finally:
  46. if response:
  47. return JsonResponse(response)
  48. return decorator
  49. @api_error_handler
  50. def get_documents(request): # TODO only here for assist
  51. filters = {
  52. 'owner': request.user
  53. }
  54. if request.GET.get('type'):
  55. filters['type'] = json.loads(request.GET.get('type'))
  56. return JsonResponse({'documents': [doc.to_dict() for doc in Document2.objects.filter(**filters)]})
  57. @api_error_handler
  58. def get_documents2(request):
  59. path = request.GET.get('path', '/') # Expects path to be a Directory for now
  60. try:
  61. file_doc = Directory.objects.get(owner=request.user, name=path) # TODO perms
  62. except Directory.DoesNotExist, e:
  63. if path == '/':
  64. file_doc = Directory.objects.create(name='/', type='directory', owner=request.user)
  65. file_doc.dependencies.add(*Document2.objects.filter(owner=request.user).exclude(id=file_doc.id))
  66. else:
  67. raise e
  68. return JsonResponse({
  69. 'file': file_doc.to_dict(),
  70. 'documents': [doc.to_dict() for doc in file_doc.documents()],
  71. 'path': path
  72. })
  73. @api_error_handler
  74. def get_document(request):
  75. if request.GET.get('id'):
  76. doc = Document2.objects.get(id=request.GET['id'])
  77. else:
  78. doc = Document2.objects.get(uuid=request.GET['uuid'])
  79. permissions = _massage_permissions(doc)
  80. doc_info = doc.to_dict()
  81. doc_info.update(permissions)
  82. # response = _massage_doc_for_json(doc, request.user, with_data=request.GET.get('with_data'))
  83. # return JsonResponse(response)
  84. return JsonResponse(doc_info)
  85. def _massage_permissions(document):
  86. """
  87. Returns the permissions for a given document as a dictionary
  88. """
  89. read_perms = document.list_permissions(perm='read')
  90. write_perms = document.list_permissions(perm='write')
  91. return {
  92. 'perms': {
  93. 'read': {
  94. 'users': [{'id': perm_user.id, 'username': perm_user.username} \
  95. for perm_user in read_perms.users.all()],
  96. 'groups': [{'id': perm_group.id, 'name': perm_group.name} \
  97. for perm_group in read_perms.groups.all()]
  98. },
  99. 'write': {
  100. 'users': [{'id': perm_user.id, 'username': perm_user.username} \
  101. for perm_user in write_perms.users.all()],
  102. 'groups': [{'id': perm_group.id, 'name': perm_group.name} \
  103. for perm_group in write_perms.groups.all()]
  104. }
  105. }
  106. }
  107. @api_error_handler
  108. @require_POST
  109. def move_document(request):
  110. source_id = request.POST.get('source_id', 'source_id')
  111. destination_id = request.POST.get('destination_id', 'destination_id')
  112. # destination exists + is dir?
  113. source = Document2.objects.document(request.user, uuid=source_id)
  114. destination = Directory.objects.document(request.user, uuid=destination_id)
  115. source.move(destination)
  116. return JsonResponse({'status': 0})
  117. @api_error_handler
  118. @require_POST
  119. def create_directory(request):
  120. parent_path = json.loads(request.POST.get('parent_path'))
  121. name = json.loads(request.POST.get('name'))
  122. parent_dir = Directory.objects.get(owner=request.user, name=parent_path)
  123. path = Hdfs.normpath(parent_path + '/' + name)
  124. file_doc = Directory.objects.create(name=path, type='directory', owner=request.user)
  125. parent_dir.dependencies.add(file_doc)
  126. return JsonResponse({
  127. 'status': 0,
  128. 'file': file_doc.to_dict()
  129. })
  130. @api_error_handler
  131. @require_POST
  132. def delete_document(request):
  133. document_id = json.loads(request.POST.get('doc_id'))
  134. skip_trash = json.loads(request.POST.get('skip_trash', 'false')) # TODO always false currently
  135. document = Document2.objects.document(request.user, doc_id=document_id)
  136. if document.type == 'directory' and document.dependencies().count() > 1:
  137. raise PopupException(_('Directory is not empty'))
  138. document.delete()
  139. return JsonResponse({
  140. 'status': 0,
  141. })
  142. @api_error_handler
  143. @require_POST
  144. def share_document(request):
  145. """
  146. Set who else or which other group can interact with the document.
  147. Example of input: {'read': {'user_ids': [1, 2, 3], 'group_ids': [1, 2, 3]}}
  148. """
  149. perms_dict = json.loads(request.POST['perms_dict'])
  150. doc_id = json.loads(request.POST['doc_id'])
  151. doc = Document2.objects.document(request.user, doc_id)
  152. for name, perm in perms_dict.iteritems():
  153. users = groups = None
  154. if perm.get('user_ids'):
  155. users = User.objects.in_bulk(perm.get('user_ids'))
  156. else:
  157. users = []
  158. if perm.get('group_ids'):
  159. groups = Group.objects.in_bulk(perm.get('group_ids'))
  160. else:
  161. groups = []
  162. doc.share(request.user, name=name, users=users, groups=groups)
  163. def _massage_doc_for_json(document, user, with_data=False):
  164. massaged_doc = {
  165. 'id': document.id,
  166. 'uuid': document.uuid,
  167. 'owner': document.owner.username,
  168. 'type': html.conditional_escape(document.type),
  169. 'name': html.conditional_escape(document.name),
  170. 'description': html.conditional_escape(document.description),
  171. 'isMine': document.owner == user,
  172. 'lastModified': document.last_modified.strftime("%x %X"),
  173. 'lastModifiedInMillis': time.mktime(document.last_modified.timetuple()),
  174. 'version': document.version,
  175. 'is_history': document.is_history,
  176. # tags
  177. # dependencies
  178. }
  179. if with_data:
  180. massaged_doc['data'] = document.data_dict
  181. return massaged_doc
  182. def export_documents(request):
  183. if request.GET.get('documents'):
  184. selection = json.loads(request.GET.get('documents'))
  185. else:
  186. selection = json.loads(request.POST.get('documents'))
  187. # If non admin, only export documents the user owns
  188. docs = Document2.objects
  189. if not request.user.is_superuser:
  190. docs = docs.filter(owner=request.user)
  191. docs = docs.filter(id__in=selection).order_by('-id')
  192. doc_ids = docs.values_list('id', flat=True)
  193. f = StringIO.StringIO()
  194. if doc_ids:
  195. doc_ids = ','.join(map(str, doc_ids))
  196. management.call_command('dumpdata', 'desktop.Document2', primary_keys=doc_ids, indent=2, use_natural_keys=True, verbosity=2, stdout=f)
  197. if request.GET.get('format') == 'json':
  198. return JsonResponse(f.getvalue(), safe=False)
  199. elif request.GET.get('format') == 'zip':
  200. zfile = zipfile.ZipFile(f, 'w')
  201. zfile.writestr("hue.json", f.getvalue())
  202. for doc in docs:
  203. if doc.type == 'notebook':
  204. try:
  205. from spark.models import Notebook
  206. zfile.writestr("notebook-%s-%s.txt" % (doc.name, doc.id), smart_str(Notebook(document=doc).get_str()))
  207. except Exception, e:
  208. print e
  209. LOG.exception(e)
  210. zfile.close()
  211. response = HttpResponse(content_type="application/zip")
  212. response["Content-Length"] = len(f.getvalue())
  213. response['Content-Disposition'] = 'attachment; filename="hue-documents.zip"'
  214. response.write(f.getvalue())
  215. return response
  216. else:
  217. return make_response(f.getvalue(), 'json', 'hue-documents')
  218. def import_documents(request):
  219. if request.FILES.get('documents'):
  220. documents = request.FILES['documents'].read()
  221. else:
  222. documents = json.loads(request.POST.get('documents'))
  223. documents = json.loads(documents)
  224. docs = []
  225. for doc in documents:
  226. if not request.user.is_superuser:
  227. doc['fields']['owner'] = [request.user.username]
  228. owner = doc['fields']['owner'][0]
  229. doc['fields']['tags'] = []
  230. if Document2.objects.filter(uuid=doc['fields']['uuid'], owner__username=owner).exists():
  231. doc['pk'] = Document2.objects.get(uuid=doc['fields']['uuid'], owner__username=owner).pk
  232. else:
  233. doc['pk'] = None
  234. docs.append(doc)
  235. f = tempfile.NamedTemporaryFile(mode='w+', suffix='.json')
  236. f.write(json.dumps(docs))
  237. f.flush()
  238. stdout = StringIO.StringIO()
  239. try:
  240. management.call_command('loaddata', f.name, stdout=stdout)
  241. except Exception, e:
  242. return JsonResponse({'message': smart_str(e)})
  243. Document.objects.sync()
  244. if request.POST.get('redirect'):
  245. return redirect(request.POST.get('redirect'))
  246. else:
  247. return JsonResponse({'message': stdout.getvalue()})