1"""Copyright 2008 Orbitz WorldWide
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7   http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License."""
14
15import re
16
17from django.conf import settings
18from django.shortcuts import render
19from django.utils.safestring import mark_safe
20from django.utils.html import escape
21from graphite.account.models import Profile
22from graphite.compat import HttpResponse
23from graphite.user_util import getProfile, getProfileByUsername
24from graphite.util import json
25from graphite.logger import log
26from hashlib import md5
27
28
29def header(request):
30  "View for the header frame of the browser UI"
31  context = {}
32  context['user'] = request.user
33  context['profile'] = getProfile(request)
34  context['documentation_url'] = settings.DOCUMENTATION_URL
35  context['login_url'] = settings.LOGIN_URL
36  return render(request, "browserHeader.html", context)
37
38
39def browser(request):
40  "View for the top-level frame of the browser UI"
41  context = {
42    'queryString': mark_safe(request.GET.urlencode()),
43    'target': request.GET.get('target')
44  }
45  if context['queryString']:
46    context['queryString'] = context['queryString'].replace('#','%23')
47  if context['target']:
48    context['target'] = context['target'].replace('#','%23') #js libs terminate a querystring on #
49  return render(request, "browser.html", context)
50
51
52def search(request):
53  query = request.POST.get('query')
54  if not query:
55    return HttpResponse("")
56
57  patterns = query.split()
58  regexes = [re.compile(p,re.I) for p in patterns]
59
60  def matches(s):
61    for regex in regexes:
62      if regex.search(s):
63        return True
64    return False
65
66  results = []
67
68  index_file = open(settings.INDEX_FILE)
69  for line in index_file:
70    if matches(line):
71      results.append( line.strip() )
72    if len(results) >= 100:
73      break
74
75  index_file.close()
76  result_string = ','.join(results)
77  return HttpResponse(result_string, content_type='text/plain')
78
79
80def myGraphLookup(request):
81  "View for My Graphs navigation"
82  profile = getProfile(request,allowDefault=False)
83  assert profile
84
85  nodes = []
86  leafNode = {
87    'allowChildren' : 0,
88    'expandable' : 0,
89    'leaf' : 1,
90  }
91  branchNode = {
92    'allowChildren' : 1,
93    'expandable' : 1,
94    'leaf' : 0,
95  }
96
97  try:
98    path = request.GET.get('path', u'')
99
100    if path:
101      if path.endswith('.'):
102        userpath_prefix = path
103
104      else:
105        userpath_prefix = path + '.'
106
107    else:
108      userpath_prefix = u""
109
110    matches = [ graph for graph in profile.mygraph_set.all().order_by('name') if graph.name.startswith(userpath_prefix) ]
111
112    log.info( "myGraphLookup: username=%s, path=%s, userpath_prefix=%s, %ld graph to process" % (profile.user.username, path, userpath_prefix, len(matches)) )
113    branch_inserted = set()
114    leaf_inserted = set()
115
116    for graph in matches: #Now let's add the matching graph
117      isBranch = False
118      dotPos = graph.name.find( '.', len(userpath_prefix) )
119
120      if dotPos >= 0:
121        isBranch = True
122        name = graph.name[ len(userpath_prefix) : dotPos ]
123        if name in branch_inserted: continue
124        branch_inserted.add(name)
125
126      else:
127         name = graph.name[ len(userpath_prefix): ]
128         if name in leaf_inserted: continue
129         leaf_inserted.add(name)
130
131      node = {'text': escape(name)}
132
133      if isBranch:
134        node.update({'id': userpath_prefix + name + '.'})
135        node.update(branchNode)
136
137      else:
138        m = md5()
139        m.update(name.encode('utf-8'))
140        node.update( { 'id' : str(userpath_prefix + m.hexdigest()), 'graphUrl' : graph.url } )
141        node.update(leafNode)
142
143      nodes.append(node)
144
145  except Exception:
146    log.exception("browser.views.myGraphLookup(): could not complete request.")
147
148  if not nodes:
149    no_graphs = { 'text' : "No saved graphs", 'id' : 'no-click' }
150    no_graphs.update(leafNode)
151    nodes.append(no_graphs)
152
153  return json_response(nodes, request)
154
155
156def userGraphLookup(request):
157  "View for User Graphs navigation"
158  user = request.GET.get('user')
159  path = request.GET['path']
160
161  if user:
162    username = user
163    graphPath = path[len(username)+1:]
164  elif '.' in path:
165    username, graphPath = path.split('.', 1)
166  else:
167    username, graphPath = path, None
168
169  nodes = []
170
171  branchNode = {
172    'allowChildren' : 1,
173    'expandable' : 1,
174    'leaf' : 0,
175  }
176  leafNode = {
177    'allowChildren' : 0,
178    'expandable' : 0,
179    'leaf' : 1,
180  }
181
182  try:
183
184    if not username:
185      profiles = Profile.objects.exclude(user__username='default').order_by('user__username')
186
187      for profile in profiles:
188        if profile.mygraph_set.count():
189          node = {
190            'text' : profile.user.username,
191            'id' : profile.user.username,
192          }
193
194          node.update(branchNode)
195          nodes.append(node)
196
197    else:
198      profile = getProfileByUsername(username)
199      assert profile, "No profile for username '%s'" % username
200
201      if graphPath:
202        prefix = graphPath.rstrip('.') + '.'
203      else:
204        prefix = ''
205
206      matches = [ graph for graph in profile.mygraph_set.order_by('name') if graph.name.startswith(prefix) ]
207      inserted = set()
208
209      for graph in matches:
210        relativePath = graph.name[ len(prefix): ]
211        nodeName = relativePath.split('.')[0]
212
213        if nodeName in inserted:
214          continue
215        inserted.add(nodeName)
216
217        if '.' in relativePath: # branch
218          node = {
219            'text' : escape(nodeName),
220            'id' : username + '.' + prefix + nodeName + '.',
221          }
222          node.update(branchNode)
223        else: # leaf
224          m = md5()
225          m.update(nodeName.encode('utf-8'))
226
227          node = {
228            'text' : escape(nodeName),
229            'id' : username + '.' + prefix + m.hexdigest(),
230            'graphUrl' : graph.url,
231          }
232          node.update(leafNode)
233
234        nodes.append(node)
235
236  except Exception:
237    log.exception("browser.views.userLookup(): could not complete request for %s" % username)
238
239  if not nodes:
240    no_graphs = { 'text' : "No saved graphs", 'id' : 'no-click' }
241    no_graphs.update(leafNode)
242    nodes.append(no_graphs)
243
244  nodes.sort(key=lambda node: node['allowChildren'], reverse = True)
245
246  return json_response(nodes, request)
247
248
249def json_response(nodes, request=None):
250  if request:
251    jsonp = request.GET.get('jsonp', False) or request.POST.get('jsonp', False)
252  else:
253    jsonp = False
254  #json = str(nodes) #poor man's json encoder for simple types
255  json_data = json.dumps(nodes)
256  if jsonp:
257    response = HttpResponse("%s(%s)" % (jsonp, json_data),
258                            content_type="text/javascript")
259  else:
260    response = HttpResponse(json_data, content_type="application/json")
261  response['Pragma'] = 'no-cache'
262  response['Cache-Control'] = 'no-cache'
263  return response
264