mirror of
https://github.com/fergalmoran/dss.git
synced 2026-01-07 17:34:21 +00:00
Added email notifications
This commit is contained in:
@@ -1,13 +1,17 @@
|
||||
import urlparse
|
||||
import re
|
||||
from django.contrib.sites.models import Site
|
||||
from django.template.defaultfilters import slugify
|
||||
|
||||
__author__ = 'fergalm'
|
||||
|
||||
|
||||
def urlclean(url):
|
||||
#remove double slashes
|
||||
ret = urlparse.urljoin(url, urlparse.urlparse(url).path.replace('//','/'))
|
||||
ret = urlparse.urljoin(url, urlparse.urlparse(url).path.replace('//', '/'))
|
||||
return ret
|
||||
|
||||
|
||||
def unique_slugify(instance, value, slug_field_name='slug', queryset=None, slug_separator='-'):
|
||||
"""
|
||||
Calculates and stores a unique slug of ``value`` for an instance.
|
||||
@@ -44,7 +48,7 @@ def unique_slugify(instance, value, slug_field_name='slug', queryset=None, slug_
|
||||
slug = original_slug
|
||||
end = '%s%s' % (slug_separator, next)
|
||||
if slug_len and len(slug) + len(end) > slug_len:
|
||||
slug = slug[:slug_len-len(end)]
|
||||
slug = slug[:slug_len - len(end)]
|
||||
slug = _slug_strip(slug, slug_separator)
|
||||
slug = '%s%s' % (slug, end)
|
||||
next += 1
|
||||
@@ -74,4 +78,13 @@ def _slug_strip(value, separator='-'):
|
||||
if separator != '-':
|
||||
re_sep = re.escape(separator)
|
||||
value = re.sub(r'^%s+|%s+$' % (re_sep, re_sep), '', value)
|
||||
return value
|
||||
return value
|
||||
|
||||
def is_absolute(url):
|
||||
return bool(urlparse.urlparse(url).scheme)
|
||||
|
||||
def wrap_full(url):
|
||||
if not is_absolute(url):
|
||||
url = "http://%s%s" % (Site.objects.get_current().domain, url)
|
||||
|
||||
return url
|
||||
@@ -171,6 +171,7 @@ INSTALLED_APPS = (
|
||||
'django_jenkins',
|
||||
'dbbackup',
|
||||
'jfu',
|
||||
'djrill',
|
||||
#'backbone_tastypie',
|
||||
)
|
||||
|
||||
@@ -229,17 +230,20 @@ SENDFILE_ROOT = os.path.join(MEDIA_ROOT, 'mixes')
|
||||
SENDFILE_URL = '/media/mixes'
|
||||
|
||||
import mimetypes
|
||||
|
||||
mimetypes.add_type("text/xml", ".plist", False)
|
||||
|
||||
HTML_MINIFY = not localsettings.DEBUG
|
||||
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
|
||||
EMAIL_HOST = localsettings.EMAIL_HOST
|
||||
EMAIL_PORT = localsettings.EMAIL_PORT
|
||||
DEFAULT_FROM_EMAIL = 'DSS ChatBot <chatbot@deepsouthsounds.com>'
|
||||
DEFAULT_HTTP_PROTOCOL = 'http'
|
||||
|
||||
EMAIL_BACKEND = 'djrill.mail.backends.djrill.DjrillBackend'
|
||||
MANDRILL_API_KEY = localsettings.MANDRILL_API_KEY
|
||||
|
||||
if DEBUG:
|
||||
import mimetypes
|
||||
|
||||
@@ -272,3 +276,4 @@ GEOIP_PATH = localsettings.GEOIP_PATH
|
||||
from pipelinesettings import *
|
||||
|
||||
|
||||
|
||||
|
||||
372
spa/ajax.py
372
spa/ajax.py
@@ -29,262 +29,262 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AjaxHandler(object):
|
||||
# Get an instance of a logger
|
||||
# Get an instance of a logger
|
||||
|
||||
def __init__(self, api_name="v1"):
|
||||
self.api_name = api_name
|
||||
def __init__(self, api_name="v1"):
|
||||
self.api_name = api_name
|
||||
|
||||
@property
|
||||
def urls(self):
|
||||
pattern_list = [
|
||||
url(r'^mix/add_comment/$', 'spa.ajax.mix_add_comment', name='mix_add_comment'),
|
||||
url(r'^mix/comments/(?P<mix_id>\d+)/$', 'spa.ajax.mix_comments', name='ajax_mix_comments'),
|
||||
url(r'^header/$', 'spa.ajax.header', name='header'),
|
||||
url(r'^session_play_count/$', 'spa.ajax.session_play_count'),
|
||||
url(r'^mix_stream_url/(?P<mix_id>\d+)/$', 'spa.ajax.get_mix_stream_url'),
|
||||
url(r'^release_player/(?P<release_id>\d+)/$', 'spa.ajax.release_player'),
|
||||
url(r'^live_now_playing/$', 'spa.ajax.live_now_playing'),
|
||||
url(r'^mark_read/$', 'spa.ajax.mark_read'),
|
||||
url(r'^facebook_post_likes_allowed/$', 'spa.ajax.facebook_post_likes_allowed',
|
||||
name='ajax_facebook_post_likes_allowed'),
|
||||
url(r'^upload_mix_image/(?P<mix_id>\d+)/$', 'spa.ajax.upload_mix_image', name='ajax_upload_mix_image'),
|
||||
url(r'^upload_release_image/(?P<release_id>\d+)/$', 'spa.ajax.upload_release_image',
|
||||
name='ajax_upload_release_image'),
|
||||
url(r'^upload_avatar_image/$', 'spa.ajax.upload_avatar_image', name='ajax_upload_avatar_image'),
|
||||
url(r'^lookup/search/$', 'spa.ajax.lookup_search', name='ajax_lookup'),
|
||||
url(r'^lookup/(?P<source>\w+)/$', 'spa.ajax.lookup', name='ajax_lookup'),
|
||||
]
|
||||
return pattern_list
|
||||
@property
|
||||
def urls(self):
|
||||
pattern_list = [
|
||||
url(r'^mix/add_comment/$', 'spa.ajax.mix_add_comment', name='mix_add_comment'),
|
||||
url(r'^mix/comments/(?P<mix_id>\d+)/$', 'spa.ajax.mix_comments', name='ajax_mix_comments'),
|
||||
url(r'^header/$', 'spa.ajax.header', name='header'),
|
||||
url(r'^session_play_count/$', 'spa.ajax.session_play_count'),
|
||||
url(r'^mix_stream_url/(?P<mix_id>\d+)/$', 'spa.ajax.get_mix_stream_url'),
|
||||
url(r'^release_player/(?P<release_id>\d+)/$', 'spa.ajax.release_player'),
|
||||
url(r'^live_now_playing/$', 'spa.ajax.live_now_playing'),
|
||||
url(r'^mark_read/$', 'spa.ajax.mark_read'),
|
||||
url(r'^facebook_post_likes_allowed/$', 'spa.ajax.facebook_post_likes_allowed',
|
||||
name='ajax_facebook_post_likes_allowed'),
|
||||
url(r'^upload_mix_image/(?P<mix_id>\d+)/$', 'spa.ajax.upload_mix_image', name='ajax_upload_mix_image'),
|
||||
url(r'^upload_release_image/(?P<release_id>\d+)/$', 'spa.ajax.upload_release_image',
|
||||
name='ajax_upload_release_image'),
|
||||
url(r'^upload_avatar_image/$', 'spa.ajax.upload_avatar_image', name='ajax_upload_avatar_image'),
|
||||
url(r'^lookup/search/$', 'spa.ajax.lookup_search', name='ajax_lookup'),
|
||||
url(r'^lookup/(?P<source>\w+)/$', 'spa.ajax.lookup', name='ajax_lookup'),
|
||||
]
|
||||
return pattern_list
|
||||
|
||||
|
||||
def _get_json(payload, key='value'):
|
||||
data = {
|
||||
key: payload
|
||||
}
|
||||
return simplejson.dumps(data)
|
||||
data = {
|
||||
key: payload
|
||||
}
|
||||
return simplejson.dumps(data)
|
||||
|
||||
|
||||
@render_to('inc/header.html')
|
||||
def header(request):
|
||||
return HttpResponse(render_to_response('inc/header.html'))
|
||||
return HttpResponse(render_to_response('inc/header.html'))
|
||||
|
||||
|
||||
def session_play_count(request):
|
||||
"""
|
||||
"""
|
||||
|
||||
:param request:
|
||||
:return: Number of tracks played in this session
|
||||
"""
|
||||
if not request.user.is_authenticated():
|
||||
if 'play_count' in request.session:
|
||||
result = simplejson.dumps({
|
||||
'play_count': request.session['play_count']
|
||||
})
|
||||
else:
|
||||
result = simplejson.dumps({
|
||||
'play_count': '0'
|
||||
})
|
||||
else:
|
||||
result = simplejson.dumps({
|
||||
'play_count': '0'
|
||||
})
|
||||
return HttpResponse(result, mimetype='application/json')
|
||||
:param request:
|
||||
:return: Number of tracks played in this session
|
||||
"""
|
||||
if not request.user.is_authenticated():
|
||||
if 'play_count' in request.session:
|
||||
result = simplejson.dumps({
|
||||
'play_count': request.session['play_count']
|
||||
})
|
||||
else:
|
||||
result = simplejson.dumps({
|
||||
'play_count': '0'
|
||||
})
|
||||
else:
|
||||
result = simplejson.dumps({
|
||||
'play_count': '0'
|
||||
})
|
||||
return HttpResponse(result, mimetype='application/json')
|
||||
|
||||
|
||||
def get_mix_stream_url(request, mix_id):
|
||||
try:
|
||||
if not request.user.is_authenticated():
|
||||
if 'play_count' in request.session:
|
||||
request.session['play_count'] += 1
|
||||
else:
|
||||
request.session['play_count'] = 1
|
||||
try:
|
||||
if not request.user.is_authenticated():
|
||||
if 'play_count' in request.session:
|
||||
request.session['play_count'] += 1
|
||||
else:
|
||||
request.session['play_count'] = 1
|
||||
|
||||
mix = Mix.objects.get(pk=mix_id)
|
||||
mix.add_play(request.user)
|
||||
data = {
|
||||
'stream_url': mix.get_stream_path(),
|
||||
'description': mix.description,
|
||||
'item_url': mix.get_absolute_url(),
|
||||
'title': mix.title
|
||||
}
|
||||
return HttpResponse(simplejson.dumps(data), mimetype="application/json")
|
||||
except Exception, e:
|
||||
logger.exception("Error getting mix stream url")
|
||||
return HttpResponse("Error getting mix stream url", status=401)
|
||||
mix = Mix.objects.get(pk=mix_id)
|
||||
mix.add_play(request.user)
|
||||
data = {
|
||||
'stream_url': mix.get_stream_path(),
|
||||
'description': mix.description,
|
||||
'item_url': mix.get_absolute_url(),
|
||||
'title': mix.title
|
||||
}
|
||||
return HttpResponse(simplejson.dumps(data), mimetype="application/json")
|
||||
except Exception, e:
|
||||
logger.exception("Error getting mix stream url")
|
||||
return HttpResponse("Error getting mix stream url", status=401)
|
||||
|
||||
|
||||
def live_now_playing(request):
|
||||
now_playing_info = live.get_now_playing(
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_URL'],
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_PORT'],
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_MOUNT'])
|
||||
try:
|
||||
if now_playing_info is not None:
|
||||
return HttpResponse(
|
||||
simplejson.dumps({
|
||||
'stream_url': 'http://%s:%s/%s' % (
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_URL'],
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_PORT'],
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_MOUNT']),
|
||||
'description': now_playing_info['stream_description'],
|
||||
'title': now_playing_info['current_song']
|
||||
}), mimetype="application/json")
|
||||
except:
|
||||
return HttpResponseNotFound(now_playing_info)
|
||||
now_playing_info = live.get_now_playing(
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_URL'],
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_PORT'],
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_MOUNT'])
|
||||
try:
|
||||
if now_playing_info is not None:
|
||||
return HttpResponse(
|
||||
simplejson.dumps({
|
||||
'stream_url': 'http://%s:%s/%s' % (
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_URL'],
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_PORT'],
|
||||
localsettings.JS_SETTINGS['LIVE_STREAM_MOUNT']),
|
||||
'description': now_playing_info['stream_description'],
|
||||
'title': now_playing_info['current_song']
|
||||
}), mimetype="application/json")
|
||||
except:
|
||||
return HttpResponseNotFound(now_playing_info)
|
||||
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
@render_to('inc/release_player.html')
|
||||
def release_player(request, release_id):
|
||||
return HttpResponse('Hello Sailor')
|
||||
return HttpResponse('Hello Sailor')
|
||||
|
||||
|
||||
def mix_add_comment(request):
|
||||
if request.POST:
|
||||
comment = Comment()
|
||||
comment.mix_id = request.POST['mixid']
|
||||
comment.user = request.user
|
||||
comment.comment = request.POST['comment']
|
||||
comment.time_index = request.POST['position']
|
||||
comment.save()
|
||||
if request.POST:
|
||||
comment = Comment()
|
||||
comment.mix_id = request.POST['mixid']
|
||||
comment.user = request.user
|
||||
comment.comment = request.POST['comment']
|
||||
comment.time_index = request.POST['position']
|
||||
comment.save()
|
||||
|
||||
return HttpResponse(_get_json('Comment posted', 'description'))
|
||||
else:
|
||||
return HttpResponse(_get_json('Error posting', 'description'))
|
||||
return HttpResponse(_get_json('Comment posted', 'description'))
|
||||
else:
|
||||
return HttpResponse(_get_json('Error posting', 'description'))
|
||||
|
||||
|
||||
@render_to('inc/comment_list.html')
|
||||
def mix_comments(request, mix_id):
|
||||
return {
|
||||
"results": Comment.objects.filter(mix_id=mix_id),
|
||||
}
|
||||
return {
|
||||
"results": Comment.objects.filter(mix_id=mix_id),
|
||||
}
|
||||
|
||||
|
||||
@login_required
|
||||
def mark_read(request):
|
||||
profile = request.user.get_profile()
|
||||
if profile is not None:
|
||||
Notification.objects.filter(to_user=profile).update(accepted_date=datetime.now())
|
||||
return HttpResponse('Success', status=200)
|
||||
pass
|
||||
profile = request.user.get_profile()
|
||||
if profile is not None:
|
||||
Notification.objects.filter(to_user=profile).update(accepted_date=datetime.now())
|
||||
return HttpResponse('Success', status=200)
|
||||
pass
|
||||
|
||||
return HttpResponse('Unauthorized', status=401)
|
||||
return HttpResponse('Unauthorized', status=401)
|
||||
|
||||
|
||||
@login_required()
|
||||
def facebook_post_likes_allowed(request):
|
||||
profile = request.user.get_profile()
|
||||
if profile is not None:
|
||||
likes_allowed = profile.activity_sharing & UserProfile.ACTIVITY_SHARE_LIKES
|
||||
facebook_allowed = profile.activity_sharing_networks & UserProfile.ACTIVITY_SHARE_NETWORK_FACEBOOK
|
||||
profile = request.user.get_profile()
|
||||
if profile is not None:
|
||||
likes_allowed = profile.activity_sharing & UserProfile.ACTIVITY_SHARE_LIKES
|
||||
facebook_allowed = profile.activity_sharing_networks & UserProfile.ACTIVITY_SHARE_NETWORK_FACEBOOK
|
||||
|
||||
return HttpResponse(_get_json(bool(likes_allowed & facebook_allowed)), mimetype="application/json")
|
||||
return HttpResponse(_get_json(bool(likes_allowed & facebook_allowed)), mimetype="application/json")
|
||||
|
||||
return HttpResponse(_get_json(False), mimetype="application/json")
|
||||
return HttpResponse(_get_json(False), mimetype="application/json")
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def upload_release_image(request, release_id):
|
||||
try:
|
||||
if 'release_image' in request.FILES and release_id is not None:
|
||||
release = Release.objects.get(pk=release_id)
|
||||
if release is not None:
|
||||
release.release_image = request.FILES['release_image']
|
||||
release.save()
|
||||
return HttpResponse(_get_json("Success"))
|
||||
except Exception, ex:
|
||||
logger.exception("Error uploading release image")
|
||||
return HttpResponse(_get_json("Failed"))
|
||||
try:
|
||||
if 'release_image' in request.FILES and release_id is not None:
|
||||
release = Release.objects.get(pk=release_id)
|
||||
if release is not None:
|
||||
release.release_image = request.FILES['release_image']
|
||||
release.save()
|
||||
return HttpResponse(_get_json("Success"))
|
||||
except Exception, ex:
|
||||
logger.exception("Error uploading release image")
|
||||
return HttpResponse(_get_json("Failed"))
|
||||
|
||||
|
||||
def upload_mix_image(request, mix_id):
|
||||
try:
|
||||
if len(request.FILES) != 0:
|
||||
mix = Mix.objects.get(pk=mix_id)
|
||||
if mix:
|
||||
mix.mix_image = request.FILES[request.FILES.keys()[0]]
|
||||
mix.save()
|
||||
return HttpResponse(json.dumps({'status': 'OK', 'url': mix.get_image_url()}))
|
||||
except Exception, ex:
|
||||
logger.exception("Error uploading avatar: %s", ex.message)
|
||||
return HttpResponse(json.dumps({'status': 'failed', 'message': ex.message}))
|
||||
try:
|
||||
if len(request.FILES) != 0:
|
||||
mix = Mix.objects.get(pk=mix_id)
|
||||
if mix:
|
||||
mix.mix_image = request.FILES[request.FILES.keys()[0]]
|
||||
mix.save()
|
||||
return HttpResponse(json.dumps({'status': 'OK', 'url': mix.get_image_url()}))
|
||||
except Exception, ex:
|
||||
logger.exception("Error uploading avatar: %s", ex.message)
|
||||
return HttpResponse(json.dumps({'status': 'failed', 'message': ex.message}))
|
||||
|
||||
return HttpResponse(json.dumps({'status': 'failed', 'message': 'No image file found'}))
|
||||
return HttpResponse(json.dumps({'status': 'failed', 'message': 'No image file found'}))
|
||||
|
||||
|
||||
def upload_avatar_image(request):
|
||||
# TODO: fergal.moran@gmail.com
|
||||
# Problem here that only the current user can update avatar
|
||||
# Might need to allow staff to change other's avatars?
|
||||
try:
|
||||
if len(request.FILES) != 0:
|
||||
profile = request.user.get_profile()
|
||||
if profile:
|
||||
profile.avatar_image = request.FILES[request.FILES.keys()[0]]
|
||||
profile.avatar_type = 'custom'
|
||||
profile.save()
|
||||
return HttpResponse(json.dumps({'status': 'OK', 'url': profile.get_avatar_image()}))
|
||||
except Exception, ex:
|
||||
logger.exception("Error uploading avatar: %s", ex.message)
|
||||
return HttpResponse(json.dumps({'status': 'failed', 'message': ex.message}))
|
||||
# TODO: fergal.moran@gmail.com
|
||||
# Problem here that only the current user can update avatar
|
||||
# Might need to allow staff to change other's avatars?
|
||||
try:
|
||||
if len(request.FILES) != 0:
|
||||
profile = request.user.get_profile()
|
||||
if profile:
|
||||
profile.avatar_image = request.FILES[request.FILES.keys()[0]]
|
||||
profile.avatar_type = 'custom'
|
||||
profile.save()
|
||||
return HttpResponse(json.dumps({'status': 'OK', 'url': profile.get_avatar_image()}))
|
||||
except Exception, ex:
|
||||
logger.exception("Error uploading avatar: %s", ex.message)
|
||||
return HttpResponse(json.dumps({'status': 'failed', 'message': ex.message}))
|
||||
|
||||
return HttpResponse(json.dumps({'status': 'failed', 'message': 'No image file found'}))
|
||||
return HttpResponse(json.dumps({'status': 'failed', 'message': 'No image file found'}))
|
||||
|
||||
|
||||
@require_POST
|
||||
@login_required
|
||||
def upload(request):
|
||||
# The assumption here is that jQuery File Upload
|
||||
# has been configured to send files one at a time.
|
||||
# If multiple files can be uploaded simultaneously,
|
||||
# 'file' may be a list of files.
|
||||
try:
|
||||
uid = request.POST['upload-hash']
|
||||
in_file = request.FILES['file'] if request.FILES else None
|
||||
fileName, extension = os.path.splitext(in_file.name)
|
||||
# The assumption here is that jQuery File Upload
|
||||
# has been configured to send files one at a time.
|
||||
# If multiple files can be uploaded simultaneously,
|
||||
# 'file' may be a list of files.
|
||||
try:
|
||||
uid = request.POST['upload-hash']
|
||||
in_file = request.FILES['file'] if request.FILES else None
|
||||
fileName, extension = os.path.splitext(in_file.name)
|
||||
|
||||
file_storage = FileSystemStorage(location=os.path.join(settings.CACHE_ROOT, "mixes"))
|
||||
cache_file = file_storage.save("%s%s" % (uid, extension), ContentFile(in_file.read()))
|
||||
file_storage = FileSystemStorage(location=os.path.join(settings.CACHE_ROOT, "mixes"))
|
||||
cache_file = file_storage.save("%s%s" % (uid, extension), ContentFile(in_file.read()))
|
||||
|
||||
create_waveform_task.delay(in_file=os.path.join(file_storage.base_location, cache_file), uid=uid)
|
||||
create_waveform_task.delay(in_file=os.path.join(file_storage.base_location, cache_file), uid=uid)
|
||||
|
||||
file_dict = {
|
||||
'size': in_file.size,
|
||||
'uid': uid
|
||||
}
|
||||
file_dict = {
|
||||
'size': in_file.size,
|
||||
'uid': uid
|
||||
}
|
||||
|
||||
return UploadResponse(request, file_dict)
|
||||
except Exception, ex:
|
||||
logger.exception(ex.message)
|
||||
raise
|
||||
return UploadResponse(request, file_dict)
|
||||
except Exception, ex:
|
||||
logger.exception(ex.message)
|
||||
raise
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def lookup_search(request):
|
||||
query = request.GET['query'] if 'query' in request.GET else request.GET['q'] if 'q' in request.GET else ''
|
||||
if query != '':
|
||||
filter_field = Mix.get_lookup_filter_field()
|
||||
kwargs = {
|
||||
'{0}__{1}'.format(filter_field, 'icontains'): query,
|
||||
}
|
||||
rows = Mix.objects.values("title").filter(**kwargs)
|
||||
#results = serializers.serialize("json", rows, fields="title",)
|
||||
results = json.dumps(rows)
|
||||
return HttpResponse(results, mimetype='application/json')
|
||||
query = request.GET['query'] if 'query' in request.GET else request.GET['q'] if 'q' in request.GET else ''
|
||||
if query != '':
|
||||
filter_field = Mix.get_lookup_filter_field()
|
||||
kwargs = {
|
||||
'{0}__{1}'.format(filter_field, 'icontains'): query,
|
||||
}
|
||||
rows = Mix.objects.values("title").filter(**kwargs)
|
||||
#results = serializers.serialize("json", rows, fields="title",)
|
||||
results = json.dumps(rows)
|
||||
return HttpResponse(results, mimetype='application/json')
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def lookup(request, source):
|
||||
query = request.GET['query'] if 'query' in request.GET else request.GET['q'] if 'q' in request.GET else ''
|
||||
if query != '':
|
||||
model = get_model('spa', source)
|
||||
if model is not None:
|
||||
filter_field = model.get_lookup_filter_field()
|
||||
kwargs = {
|
||||
'{0}__{1}'.format(filter_field, 'icontains'): query,
|
||||
}
|
||||
rows = model.objects.filter(**kwargs)
|
||||
results = json.to_ajax(rows, filter_field)
|
||||
return HttpResponse(simplejson.dumps(results), mimetype='application/json')
|
||||
return HttpResponse(_get_json("Key failure in lookup"), mimetype='application/json')
|
||||
query = request.GET['query'] if 'query' in request.GET else request.GET['q'] if 'q' in request.GET else ''
|
||||
if query != '':
|
||||
model = get_model('spa', source)
|
||||
if model is not None:
|
||||
filter_field = model.get_lookup_filter_field()
|
||||
kwargs = {
|
||||
'{0}__{1}'.format(filter_field, 'icontains'): query,
|
||||
}
|
||||
rows = model.objects.filter(**kwargs)
|
||||
results = json.to_ajax(rows, filter_field)
|
||||
return HttpResponse(simplejson.dumps(results), mimetype='application/json')
|
||||
return HttpResponse(_get_json("Key failure in lookup"), mimetype='application/json')
|
||||
|
||||
@@ -61,18 +61,6 @@ class UserResource(BackboneCompatibleResource):
|
||||
|
||||
return semi_filtered
|
||||
|
||||
def _patch_resource(self, bundle):
|
||||
#Handle the patched items from backbone
|
||||
if 'is_following' in bundle.data:
|
||||
if bundle.data['is_following']:
|
||||
bundle.obj.add_follower(bundle.request.user.get_profile())
|
||||
activity = ActivityFollow()
|
||||
activity.user = bundle.request.user.get_profile()
|
||||
activity.to_user = bundle.obj
|
||||
activity.save()
|
||||
else:
|
||||
bundle.obj.remove_follower(bundle.request.user.get_profile())
|
||||
|
||||
def obj_update(self, bundle, skip_errors=False, **kwargs):
|
||||
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding field 'Notification.notification_html'
|
||||
db.add_column(u'spa_notification', 'notification_html',
|
||||
self.gf('django.db.models.fields.CharField')(default='', max_length=1024),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'Notification.notification_html'
|
||||
db.delete_column(u'spa_notification', 'notification_html')
|
||||
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'spa._lookup': {
|
||||
'Meta': {'object_name': '_Lookup'},
|
||||
'description': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'spa.activity': {
|
||||
'Meta': {'object_name': 'Activity'},
|
||||
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.UserProfile']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'spa.activitycomment': {
|
||||
'Meta': {'object_name': 'ActivityComment', '_ormbases': ['spa.Activity']},
|
||||
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_comments'", 'to': "orm['spa.Mix']"})
|
||||
},
|
||||
'spa.activitydownload': {
|
||||
'Meta': {'object_name': 'ActivityDownload', '_ormbases': ['spa.Activity']},
|
||||
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_downloads'", 'to': "orm['spa.Mix']"})
|
||||
},
|
||||
'spa.activityfavourite': {
|
||||
'Meta': {'object_name': 'ActivityFavourite', '_ormbases': ['spa.Activity']},
|
||||
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_favourites'", 'to': "orm['spa.Mix']"})
|
||||
},
|
||||
'spa.activityfollow': {
|
||||
'Meta': {'object_name': 'ActivityFollow', '_ormbases': ['spa.Activity']},
|
||||
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'to_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_follow'", 'to': "orm['spa.UserProfile']"})
|
||||
},
|
||||
'spa.activitylike': {
|
||||
'Meta': {'object_name': 'ActivityLike', '_ormbases': ['spa.Activity']},
|
||||
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_likes'", 'to': "orm['spa.Mix']"})
|
||||
},
|
||||
'spa.activityplay': {
|
||||
'Meta': {'object_name': 'ActivityPlay', '_ormbases': ['spa.Activity']},
|
||||
u'activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa.Activity']", 'unique': 'True', 'primary_key': 'True'}),
|
||||
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_plays'", 'to': "orm['spa.Mix']"})
|
||||
},
|
||||
'spa.chatmessage': {
|
||||
'Meta': {'object_name': 'ChatMessage'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'message': ('django.db.models.fields.TextField', [], {}),
|
||||
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'chat_messages'", 'null': 'True', 'to': "orm['spa.UserProfile']"})
|
||||
},
|
||||
'spa.comment': {
|
||||
'Meta': {'object_name': 'Comment'},
|
||||
'comment': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
|
||||
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'mix': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['spa.Mix']"}),
|
||||
'time_index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'spa.event': {
|
||||
'Meta': {'object_name': 'Event'},
|
||||
'attendees': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'attendees'", 'symmetrical': 'False', 'to': u"orm['auth.User']"}),
|
||||
'date_created': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2014, 1, 25, 0, 0)'}),
|
||||
'event_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2014, 1, 25, 0, 0)'}),
|
||||
'event_description': ('tinymce.models.HTMLField', [], {}),
|
||||
'event_recurrence': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.Recurrence']"}),
|
||||
'event_time': ('django.db.models.fields.TimeField', [], {'default': 'datetime.datetime(2014, 1, 25, 0, 0)'}),
|
||||
'event_title': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
|
||||
'event_venue': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.Venue']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'spa.genre': {
|
||||
'Meta': {'object_name': 'Genre'},
|
||||
'description': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
|
||||
},
|
||||
'spa.label': {
|
||||
'Meta': {'object_name': 'Label'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'spa.mix': {
|
||||
'Meta': {'object_name': 'Mix'},
|
||||
'description': ('django.db.models.fields.TextField', [], {}),
|
||||
'download_allowed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'duration': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'favourites': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'favourites'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}),
|
||||
'filetype': ('django.db.models.fields.CharField', [], {'default': "'mp3'", 'max_length': '10'}),
|
||||
'genres': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['spa.Genre']", 'symmetrical': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_featured': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'likes': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'likes'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}),
|
||||
'mix_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
|
||||
'uid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '38', 'blank': 'True'}),
|
||||
'upload_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 1, 25, 0, 0)'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mixes'", 'to': "orm['spa.UserProfile']"}),
|
||||
'waveform_generated': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
|
||||
},
|
||||
'spa.notification': {
|
||||
'Meta': {'object_name': 'Notification'},
|
||||
'accepted_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
|
||||
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'from_user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'notifications'", 'null': 'True', 'to': "orm['spa.UserProfile']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'notification_html': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
|
||||
'notification_text': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
|
||||
'notification_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
|
||||
'target': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
|
||||
'to_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'to_notications'", 'to': "orm['spa.UserProfile']"}),
|
||||
'verb': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'})
|
||||
},
|
||||
'spa.purchaselink': {
|
||||
'Meta': {'object_name': 'PurchaseLink'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'provider': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'track': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'purchase_link'", 'to': "orm['spa.Tracklist']"}),
|
||||
'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
|
||||
},
|
||||
'spa.recurrence': {
|
||||
'Meta': {'object_name': 'Recurrence', '_ormbases': ['spa._Lookup']},
|
||||
u'_lookup_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa._Lookup']", 'unique': 'True', 'primary_key': 'True'})
|
||||
},
|
||||
'spa.release': {
|
||||
'Meta': {'object_name': 'Release'},
|
||||
'embed_code': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'release_artist': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'release_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2014, 1, 25, 0, 0)'}),
|
||||
'release_description': ('django.db.models.fields.TextField', [], {}),
|
||||
'release_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
|
||||
'release_label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.Label']"}),
|
||||
'release_title': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.UserProfile']"})
|
||||
},
|
||||
'spa.releaseaudio': {
|
||||
'Meta': {'object_name': 'ReleaseAudio'},
|
||||
'description': ('django.db.models.fields.TextField', [], {}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'release': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'release_audio'", 'null': 'True', 'to': "orm['spa.Release']"})
|
||||
},
|
||||
'spa.tracklist': {
|
||||
'Meta': {'object_name': 'Tracklist'},
|
||||
'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'index': ('django.db.models.fields.SmallIntegerField', [], {}),
|
||||
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tracklist'", 'to': "orm['spa.Mix']"}),
|
||||
'remixer': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'timeindex': ('django.db.models.fields.TimeField', [], {'null': 'True'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
|
||||
},
|
||||
'spa.userprofile': {
|
||||
'Meta': {'object_name': 'UserProfile'},
|
||||
'activity_sharing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'activity_sharing_networks': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'avatar_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'social'", 'max_length': '15'}),
|
||||
'city': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
|
||||
'country': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}),
|
||||
'display_name': ('django.db.models.fields.CharField', [], {'max_length': '35', 'blank': 'True'}),
|
||||
'following': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'followers'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_known_session': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'default': 'None', 'max_length': '50', 'null': 'True', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'userprofile'", 'unique': 'True', 'to': u"orm['auth.User']"})
|
||||
},
|
||||
'spa.venue': {
|
||||
'Meta': {'object_name': 'Venue'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
|
||||
'venue_address': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
|
||||
'venue_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
|
||||
'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '250'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['spa']
|
||||
@@ -1,11 +1,13 @@
|
||||
import threading
|
||||
import abc
|
||||
|
||||
from django.db import models
|
||||
from model_utils.managers import InheritanceManager
|
||||
from core.realtime.activity import post_activity
|
||||
from core.utils.url import wrap_full
|
||||
|
||||
from spa.models.notification import Notification
|
||||
from spa.models.userprofile import UserProfile
|
||||
from spa.models._basemodel import _BaseModel
|
||||
import abc
|
||||
|
||||
|
||||
ACTIVITYTYPES = (
|
||||
('p', 'played'),
|
||||
@@ -30,7 +32,15 @@ class Activity(_BaseModel):
|
||||
notification.notification_text = "%s %s %s" % (
|
||||
self.user.get_nice_name() or "Anonymouse", self.get_verb_past(), self.get_object_name_for_notification())
|
||||
|
||||
notification.notification_url = self.get_object_url()
|
||||
notification.notification_html = "<a href='%s'>%s</a> %s <a href='%s'>%s</a>" % (
|
||||
wrap_full(self.user.get_profile_url() or "http://deepsounds.com"),
|
||||
self.user.get_nice_name() or "Anonymouse",
|
||||
self.get_verb_past(),
|
||||
wrap_full(self.get_object_url()),
|
||||
self.get_object_name_for_notification()
|
||||
)
|
||||
|
||||
notification.notification_url = wrap_full(self.get_object_url())
|
||||
notification.verb = self.get_verb_past()
|
||||
notification.target = self.get_object_name()
|
||||
notification.save()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from spa.models import _BaseModel, UserProfile
|
||||
from spa.models.notification import Notification
|
||||
|
||||
from spa.models import _BaseModel
|
||||
from spa.models.mix import Mix
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
from django.contrib.auth.models import User
|
||||
import threading
|
||||
import mandrill
|
||||
|
||||
from django.db import models
|
||||
from django.template import loader, Context
|
||||
|
||||
from core.realtime.notification import post_notification
|
||||
from spa.models import _BaseModel, UserProfile
|
||||
from dss import localsettings
|
||||
from spa.models import _BaseModel
|
||||
|
||||
|
||||
class NotificationThread(threading.Thread):
|
||||
@@ -18,11 +22,12 @@ class NotificationThread(threading.Thread):
|
||||
|
||||
|
||||
class Notification(_BaseModel):
|
||||
to_user = models.ForeignKey(UserProfile, related_name='to_notications')
|
||||
from_user = models.ForeignKey(UserProfile, related_name='notifications', null=True, blank=True)
|
||||
to_user = models.ForeignKey('spa.UserProfile', related_name='to_notications')
|
||||
from_user = models.ForeignKey('spa.UserProfile', related_name='notifications', null=True, blank=True)
|
||||
date = models.DateTimeField(auto_now=True)
|
||||
|
||||
notification_text = models.CharField(max_length=1024)
|
||||
notification_html = models.CharField(max_length=1024)
|
||||
notification_url = models.URLField(null=True)
|
||||
|
||||
verb = models.CharField(max_length=200, null=True)
|
||||
@@ -32,3 +37,40 @@ class Notification(_BaseModel):
|
||||
|
||||
def get_notification_url(self):
|
||||
return '/api/v1/notification/%s' % self.id
|
||||
|
||||
def save(self, force_insert=False, force_update=False, using=None,
|
||||
update_fields=None):
|
||||
|
||||
self.send_notification_email()
|
||||
return super(Notification, self).save(force_insert, force_update, using, update_fields)
|
||||
|
||||
def send_notification_email(self):
|
||||
try:
|
||||
t = loader.get_template('email/notification/new.html')
|
||||
c = Context({
|
||||
'user_name': self.to_user.get_nice_name(),
|
||||
'notification_html': self.notification_html,
|
||||
'title': self.notification_html
|
||||
})
|
||||
rendered = t.render(c)
|
||||
|
||||
mandrill_client = mandrill.Mandrill(localsettings.MANDRILL_API_KEY)
|
||||
message = {
|
||||
'inline_css': True,
|
||||
'from_email': 'chatbot@deepsouthsounds.com',
|
||||
'from_name': 'DSS ChatBot',
|
||||
'headers': {'Reply-To': 'chatbot@deepsouthsounds.com'},
|
||||
'metadata': {'website': 'www.deepsouthsounds.com'},
|
||||
'subject': self.notification_text,
|
||||
'to': [{'email': 'fergal.moran@gmail.com',
|
||||
'name': 'Fergal Moran',
|
||||
'type': 'to'}],
|
||||
'html': rendered,
|
||||
'text': 'Get yourself some HTML man!',
|
||||
}
|
||||
|
||||
result = mandrill_client.messages.send(message=message, async=False)
|
||||
print result
|
||||
|
||||
except mandrill.Error, e: # Mandrill errors are thrown as exceptions
|
||||
print 'A mandrill error occurred: %s - %s' % (e.__class__, e)
|
||||
|
||||
@@ -7,15 +7,17 @@ from django.db import models
|
||||
from django.db.models import Count
|
||||
from django_gravatar.helpers import has_gravatar, get_gravatar_url
|
||||
from sorl.thumbnail import get_thumbnail
|
||||
|
||||
from allauth.socialaccount.models import SocialAccount
|
||||
from core.utils.file import generate_save_file_name
|
||||
from core.utils.url import unique_slugify
|
||||
from dss import settings
|
||||
from spa.models._basemodel import _BaseModel
|
||||
from templated_email import send_templated_mail
|
||||
from sorl import thumbnail
|
||||
from sorl.thumbnail.helpers import ThumbnailError
|
||||
|
||||
from core.utils.file import generate_save_file_name
|
||||
from core.utils.url import unique_slugify, wrap_full
|
||||
from dss import settings
|
||||
from spa.models.notification import Notification
|
||||
from spa.models._basemodel import _BaseModel
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -109,24 +111,6 @@ class UserProfile(_BaseModel):
|
||||
except Exception, ex:
|
||||
self.logger.error("Exception updating favourite: %s" % ex.message)
|
||||
|
||||
def add_follower(self, user):
|
||||
self.followers.add(user)
|
||||
user.following.add(self)
|
||||
|
||||
if not settings.DEBUG:
|
||||
try:
|
||||
send_templated_email([user.user], "notification/new_follower", {"profile": self.user})
|
||||
except Exception, ex:
|
||||
self.logger.error("Unable to send email for new follower")
|
||||
self.logger.error("Exception: %s" % ex.message)
|
||||
self.logger.error("Host: %s" % settings.EMAIL_HOST)
|
||||
self.logger.error("Port: %s" % settings.EMAIL_PORT)
|
||||
self.logger.error("Backend: %s" % settings.EMAIL_BACKEND)
|
||||
|
||||
def remove_follower(self, user):
|
||||
if user in self.followers.all():
|
||||
self.followers.remove(user)
|
||||
|
||||
def is_follower(self, user):
|
||||
try:
|
||||
return user.get_profile() in self.followers.all()
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from django.contrib.sessions.models import Session
|
||||
from django.core import signals
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.models.signals import post_save, pre_save
|
||||
from django.db.models.signals import post_save, pre_save, m2m_changed
|
||||
from django.dispatch import Signal, receiver
|
||||
from django.contrib.auth.models import User
|
||||
from core.utils.audio.mp3 import mp3_length
|
||||
from core.utils.url import wrap_full
|
||||
from spa.models import Notification
|
||||
|
||||
from spa.models.userprofile import UserProfile
|
||||
from spa.models.mix import Mix
|
||||
@@ -97,3 +99,29 @@ def session_pre_save(sender, **kwargs):
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
|
||||
|
||||
@receiver(m2m_changed, sender=UserProfile.following.through, dispatch_uid='user_followers_changed')
|
||||
def user_followers_changed(sender, **kwargs):
|
||||
try:
|
||||
if kwargs['action'] == 'post_add':
|
||||
source_user = kwargs['instance']
|
||||
if source_user:
|
||||
for i in kwargs['pk_set']:
|
||||
target_user = UserProfile.objects.get(pk=i)
|
||||
if target_user:
|
||||
notification = Notification()
|
||||
notification.from_user = source_user
|
||||
notification.to_user = target_user
|
||||
notification.notification_text = "You have a new follower on Deep South Sounds"
|
||||
|
||||
notification.notification_html = "<a href='%s'>%s</a> followed you on <a href='http://deepsouthsounds.com'>Deep South Sounds</a>" % (
|
||||
wrap_full(source_user.get_profile_url()),
|
||||
source_user.get_nice_name()
|
||||
)
|
||||
|
||||
notification.notification_url = wrap_full(source_user.get_absolute_url())
|
||||
notification.verb = "followed"
|
||||
notification.target = "Lick my balls"
|
||||
notification.save()
|
||||
except Exception, ex:
|
||||
print "Error sending new follower: %s" % ex.message
|
||||
0
templates/notification/email_body.txt → templates/email/email_body.txt
Executable file → Normal file
0
templates/notification/email_body.txt → templates/email/email_body.txt
Executable file → Normal file
404
templates/email/notification/new.html
Normal file
404
templates/email/notification/new.html
Normal file
@@ -0,0 +1,404 @@
|
||||
{% block content %}
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<!-- If you delete this meta tag, Half Life 3 will never be released. -->
|
||||
<meta name="viewport" content="width=device-width"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<title>{{ title }}</title>
|
||||
<style type="text/css">
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.collapse {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-text-size-adjust: none;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #2BA6CB;
|
||||
}
|
||||
|
||||
.btn {
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
background-color: #666;
|
||||
padding: 10px 16px;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
p.callout {
|
||||
padding: 15px;
|
||||
background-color: #ECF8FF;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.callout a {
|
||||
font-weight: bold;
|
||||
color: #2BA6CB;
|
||||
}
|
||||
|
||||
table.social {
|
||||
/* padding:15px; */
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
|
||||
.social .soc-btn {
|
||||
padding: 3px 7px;
|
||||
font-size: 12px;
|
||||
margin-bottom: 10px;
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a.fb {
|
||||
background-color: #3B5998 !important;
|
||||
}
|
||||
|
||||
a.tw {
|
||||
background-color: #1daced !important;
|
||||
}
|
||||
|
||||
a.gp {
|
||||
background-color: #DB4A39 !important;
|
||||
}
|
||||
|
||||
a.ms {
|
||||
background-color: #000 !important;
|
||||
}
|
||||
|
||||
.sidebar .soc-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.head-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header.container table td.logo {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.header.container table td.label {
|
||||
padding: 15px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
table.body-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.footer-wrap {
|
||||
width: 100%;
|
||||
clear: both !important;
|
||||
}
|
||||
|
||||
.footer-wrap .container td.content p {
|
||||
border-top: 1px solid rgb(215, 215, 215);
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.footer-wrap .container td.content p {
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 15px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
h1 small, h2 small, h3 small, h4 small, h5 small, h6 small {
|
||||
font-size: 60%;
|
||||
color: #6f6f6f;
|
||||
line-height: 0;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 200;
|
||||
font-size: 44px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 200;
|
||||
font-size: 37px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: 500;
|
||||
font-size: 27px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-weight: 500;
|
||||
font-size: 23px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-weight: 900;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-weight: 900;
|
||||
font-size: 14px;
|
||||
text-transform: uppercase;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.collapse {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
p, ul {
|
||||
margin-bottom: 10px;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
p.lead {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
p.last {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
margin-left: 5px;
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
ul.sidebar {
|
||||
background: #ebebeb;
|
||||
display: block;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
ul.sidebar li {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul.sidebar li a {
|
||||
text-decoration: none;
|
||||
color: #666;
|
||||
padding: 10px 16px;
|
||||
/* font-weight:bold; */
|
||||
margin-right: 10px;
|
||||
/* text-align:center; */
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #777777;
|
||||
border-top: 1px solid #FFFFFF;
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul.sidebar li a.last {
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
|
||||
ul.sidebar li a h1, ul.sidebar li a h2, ul.sidebar li a h3, ul.sidebar li a h4, ul.sidebar li a h5, ul.sidebar li a h6, ul.sidebar li a p {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: block !important;
|
||||
max-width: 600px !important;
|
||||
margin: 0 auto !important; /* makes it centered */
|
||||
clear: both !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 15px;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.content table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.column {
|
||||
width: 300px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.column tr td {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.column-wrap {
|
||||
padding: 0 !important;
|
||||
margin: 0 auto;
|
||||
max-width: 600px !important;
|
||||
}
|
||||
|
||||
.column table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.social .column {
|
||||
width: 280px;
|
||||
min-width: 279px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.clear {
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
a[class="btn"] {
|
||||
display: block !important;
|
||||
margin-bottom: 10px !important;
|
||||
background-image: none !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
div[class="column"] {
|
||||
width: auto !important;
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
table.social div[class="column"] {
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body bgcolor="#FFFFFF">
|
||||
<!-- HEADER -->
|
||||
<table class="head-wrap" bgcolor="#999999">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="header container">
|
||||
<div class="content">
|
||||
<table bgcolor="#999999">
|
||||
<tr>
|
||||
<td><img src="http://t-static.deepsouthsounds.com/img/default-avatar-128.png"/></td>
|
||||
<td align="right"><h6 class="collapse">New Notice From Deep South Sounds</h6></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- /HEADER -->
|
||||
<!-- BODY -->
|
||||
<table class="body-wrap">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="container" bgcolor="#FFFFFF">
|
||||
<div class="content">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<h3>Hi, {{ user_name }}</h3>
|
||||
|
||||
<p class="lead">{{ html_title }}</p>
|
||||
<p class="callout">
|
||||
{{ notification_html |safe }}
|
||||
</p><!-- /Callout Panel -->
|
||||
<!-- social & contact -->
|
||||
<table class="social" width="100%">
|
||||
<tr>
|
||||
<td>
|
||||
<!-- column 1 -->
|
||||
<table align="left" class="column">
|
||||
<tr>
|
||||
<td>
|
||||
<h5 class="">Connect with Us:</h5>
|
||||
|
||||
<p class=""><a
|
||||
href="https://www.facebook.com/pages/Deepsouthsoundscom/134649039964485"
|
||||
class="soc-btn fb">Facebook</a> <a href="#"
|
||||
class="soc-btn tw">Twitter</a>
|
||||
<a href="https://twitter.com/DeepSouthSounds"
|
||||
class="soc-btn gp">Google+</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table align="left" class="column">
|
||||
<tr>
|
||||
<td>
|
||||
<h5 class="">Contact Info:</h5>
|
||||
<p>
|
||||
Email: <strong><a href="emailto:chatbot@deepsouthsounds.com">chatbot@deepsouthsounds.com</a></strong>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<span class="clear"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="footer-wrap">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="container">
|
||||
<!-- content -->
|
||||
<div class="content">
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<p>
|
||||
<a href="http://deepsouthsounds.com/me">
|
||||
<unsubscribe>Manage Notifications</unsubscribe>
|
||||
</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /content -->
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- /FOOTER -->
|
||||
</body>
|
||||
</html>
|
||||
{% endblock %}
|
||||
@@ -1,5 +0,0 @@
|
||||
<a href="http://{{ current_site}}/{{ profile.get_absolute_url }}">{{ profile.get_nice_name }}</a> has started following you on Deep South Sounds. <br />
|
||||
This means they will get notifications about new mixes you upload. <br />
|
||||
<br />
|
||||
To see other notices change how you receive notifications, please go to http://{{ current_site }}/me
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{{ profile.get_nice_name }} has started following you.
|
||||
@@ -1 +0,0 @@
|
||||
<a href="{{ profile.get_absolute_url }}">{{ profile.get_nice_name }}</a> has started following you.
|
||||
@@ -1,7 +0,0 @@
|
||||
{% load i18n %}
|
||||
{% url invitations as invitation_page %}
|
||||
{% url profile_detail username=invitation.from_user.username as user_url %}
|
||||
{% blocktrans with invitation.from_user as invitation_from_user %}
|
||||
<a href="{{ user_url }}">{{ invitation_from_user }}</a> has started following you on Deep South Sounds
|
||||
(see <a href="{{ invitation_page }}">followers</a>)
|
||||
{% endblocktrans %}
|
||||
@@ -1 +0,0 @@
|
||||
You have a new follower
|
||||
Reference in New Issue
Block a user