Skip to content

Commit

Permalink
Show added artists
Browse files Browse the repository at this point in the history
  • Loading branch information
alexkay committed Oct 9, 2011
1 parent bf102d4 commit 57ddf35
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 1 deletion.
5 changes: 5 additions & 0 deletions app/models.py
Expand Up @@ -33,6 +33,11 @@ class Artist(models.Model):
country = models.CharField(max_length=2)
disambiguation = models.CharField(max_length=512)

@classmethod
def get_by_user(cls, user):
# TODO: paging
return cls.objects.filter(userartist__user__id=user.id).order_by('sort_name')[:1000]

class ReleaseGroup(models.Model):

artist = models.ForeignKey(Artist)
Expand Down
36 changes: 36 additions & 0 deletions app/tools.py
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2009-2011 Alexander Kojevnikov <alexander@kojevnikov.com>
#
# muspy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# muspy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with muspy. If not, see <http://www.gnu.org/licenses/>.

def arrange_for_table(items, columns):
"""Prepare a list of items to show it in a table.
Sort the items by columns, fill by rows. Return a list of rows.
The algorithm is a bit tricky to allow for example this:
|0 2 3| instead of |0 2 .|
|1 . .| |1 3 .|
"""
L = len(items)
N = columns
M = 1 + (L - 1) // N
return [[items[i + j * M -
# Next line compensates for empty cells in the last row.
max(0, j - L % N if i < M - 1 and L % N else 0)]
if i * N + j < L else None
for j in xrange(N)]
for i in xrange(M)]
66 changes: 66 additions & 0 deletions app/views.py
Expand Up @@ -28,6 +28,7 @@
from app.blog import articles, get_posts
from app.forms import *
from app.models import *
from app.tools import arrange_for_table

def activate(request):
if 'code' in request.GET:
Expand Down Expand Up @@ -62,6 +63,71 @@ def article(request, slug):
'is_blog': is_blog, 'root': root,
'template_name': template_name})

@login_required
def artists(request):
artists = Artist.get_by_user(request.user)

COLUMNS = 3
artist_rows = arrange_for_table(artists, COLUMNS)

# Using REQUEST because this handler can be called using both GET and POST.
# TODO: where does it POST from?
search = request.REQUEST.get('search', '')
dontadd = request.REQUEST.get('dontadd', '')
offset = request.REQUEST.get('offset', '')
offset = int(offset) if offset.isdigit() else 0

found_artists, count = [], 0
if search:
if len(search) > 16384:
messages.error('The search string is too long.')
return redirect('/artists')

if ',' in search and not offset:
# Batch add mode.
Job.add_artists(request.user.key().id(), search, dontadd)
messages.info('Your artists will be processed in the next couple of '
'minutes. In the meantime you can add more artists.')
return redirect('/artists')

found_artists, count = mb.search_artists(search, offset=offset)
if found_artists is None:
flash_error('The search server could not fulfill your request '
'due to an internal error. Please try again later.')
# TODO: no locals()
return render(request, 'artists.html', locals())

only_one = len(found_artists) == 1
first_is_exact = (len(found_artists) > 1 and
found_artists[0]['name'].lower() == search.lower() and
found_artists[1]['name'].lower() != search.lower())
if not dontadd and not offset and (only_one or first_is_exact):
# Only one artist found - add it right away.
artist_data = found_artists[0]
artist_id = artist_data['id']
artist = Artist.find(artist_id)
if not artist:
artist = Artist.add(artist_id,
artist_data['name'],
artist_data['sort-name'])
Job.add_releases(artist_id)

UserArtist.add(request.user, artist)
Job.copy_releases(artist_id, request.user.key().id())

flash_notice("%s has been added!" % artist.name)
return HttpResponseRedirect('/artists')

artists_offset = offset + len(found_artists)
artists_left = max(0, count - artists_offset)

# importing = Job.importing_artists(request.user.key().id())
# pending = sorted(s.search for s in request.user.searches.fetch(200))
# pending_rows = arrange_for_table(pending, COLUMNS)

return render(request, 'artists.html', {
'artist_rows': artist_rows})

def blog(request):
posts = get_posts()
root = request.build_absolute_uri('/')
Expand Down
1 change: 1 addition & 0 deletions db/muspy.sql
Expand Up @@ -57,6 +57,7 @@ CREATE TABLE "django_session" (
"session_data" text NOT NULL,
"expire_date" datetime NOT NULL
);
CREATE INDEX "app_artist_sort_name" ON "app_artist" ("sort_name");
CREATE INDEX "app_releasegroup_artist_id" ON "app_releasegroup" ("artist_id");
CREATE INDEX "app_userartist_artist_id" ON "app_userartist" ("artist_id");
CREATE INDEX "app_userartist_user_id" ON "app_userartist" ("user_id");
Expand Down
147 changes: 147 additions & 0 deletions templates/artists.html
@@ -0,0 +1,147 @@
{% extends "base.html" %}
{% block title %}Artists{% endblock %}
{% block header %}Artists{% endblock %}
{% block content %}

{% if search %}
{% if not found_artists %}
<p>No artists matching your query.</p>
{% else %}
<p>Search results: <small>(click <img class="add_artist" src="/static/add.gif" alt="+"/> to follow the artist)</small></p>
<div id="found_artists">
<ul>
{% for artist in found_artists %}
<li{% if artist.best_match %} class="best_match"{% endif %}>
<a class="noborder" href="/artists-add?id={{ artist.id }}&amp;search={{ search|urlencode }}"><img class="add_artist" src="/static/add.gif" alt="+" title="Add"/></a>
<a href="/artist/{{ artist.id }}">{{ artist.name }}</a>
{% if artist.disambiguation %}&nbsp;<small>({{ artist.disambiguation }})</small>{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% if artists_left %}
<p><a href="/artists?search={{ search|urlencode }}{% if dontadd %}&amp;dontadd=1{% endif %}&amp;offset={{ artists_offset }}">{{ artists_left }} more artist{{ artists_left|pluralize }}</a></p>
{% endif %}
{% endif %}
{% endif %}

<form action="/artists" method="post">
<p>
Add a new artist or artists:<br/>
<input type="text" id="search" name="search" size="50" maxlength="16384" value="{{ search }}"/>
&nbsp;<input type="submit" id="add" value="{% if dontadd %}Search{% else %}Add{% endif %}"/>
&nbsp;<input type="checkbox" id="dontadd" name="dontadd" value="1"{% if dontadd %} checked="checked"{% endif %}/>
<label for="dontadd">Search, don't add</label>
</p>
<p>
<small><strong>Hint:</strong> you can enter a comma-separated list of artists, for example: <em>Agalloch, Slumber, Lumsk</em></small>
</p>
<p>You can also <a href="/import">import artists</a> from your Last.fm profile.</p>
</form>

{% if importing %}
<h2>Importing</h2>
<p>These artists will be imported shortly:</p>
<p>{{ importing }}</p>
{% endif %}

{% if pending %}
<h2>Pending artists</h2>
<p>These artists either could not be found or their names are ambiguous. <br/>
Click on a name to search or select and remove the artists you don't want to follow.</p>
<form id="form-pending" action="/artists-remove" method="post">
<table id="pending">
{% for row in pending_rows %}
<tr>
{% for name in row %}
<td>
{% if name %}
<input type="checkbox" name="name" value="{{ name }}"/>&nbsp;<a href="/artists?search={{ name|urlencode }}&amp;dontadd=1">{{ name }}</a>
{% else %}
&nbsp;
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<div id="div-name-all"></div>
<p><input type="submit" value="Remove"/> selected names</p>
</form>
{% endif %}

<h2>Artists you are following:</h2>
{% if artist_rows %}
<form id="form-artists" action="/artists-remove" method="post">
<table id="artists">
{% for row in artist_rows %}
<tr>
{% for artist in row %}
<td>
{% if artist %}
<input type="checkbox" name="id" value="{{ artist.artist_id }}"/>&nbsp;<a href="/artist/{{ artist.artist_id }}">{{ artist.name }}</a>
{% else %}
&nbsp;
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<div id="div-id-all"></div>
<p><input type="submit" value="Remove"/> selected artists</p>
</form>
{% else %}
<p>You don't have any! Use the text box above to add your favourite artists
to the list. You will receive a notification when one of the artists that you
follow releases a new album.</p>
{% endif %}

{% endblock content %}

{% block javascript %}
<script type="text/javascript" src="/static/jquery-1.2.6.js"></script>
<script type="text/javascript">
//<![CDATA[
$(function() {
$("#search").focus();

$("#dontadd").click(function() {
$("#add").val($("#dontadd").attr("checked") ? "Search" : "Add");
});

$("#div-name-all").html('<p><input type="checkbox" id="name-all"/>&nbsp;<label for="name-all">Select all</label></p>');

$("#div-id-all").html('<p><input type="checkbox" id="id-all"/>&nbsp;<label for="id-all">Select all</label></p>');

$("#name-all").click(function() {
var checked_status = this.checked;
$("input[@name=name]").each(function() {
this.checked = checked_status;
});
});

$("#id-all").click(function() {
var checked_status = this.checked;
$("input[@name=id]").each(function() {
this.checked = checked_status;
});
});

$("#form-pending").submit(function() {
var checked = $("input[@name=name]:checked").length;
if(checked < 2) return true;
return confirm("Are you sure you want to remove " +
checked.toString() + " artists?");
});

$("#form-artists").submit(function() {
var checked = $("input[@name=id]:checked").length;
if(checked < 2) return true;
return confirm("Are you sure you want to remove " +
checked.toString() + " artists?");
});
});
//]]>
</script>
{% endblock %}
2 changes: 1 addition & 1 deletion urls.py
Expand Up @@ -25,7 +25,7 @@
(r'^$', 'index'),
(r'^activate$', 'activate'),
# (r'^artist/([0-9a-f\-]+)$', 'artist'),
# (r'^artists$', 'artists'),
(r'^artists$', 'artists'),
# (r'^artists-add$', 'artists_add'),
# (r'^artists-remove$', 'artists_remove'),
(r'^blog$', 'blog'),
Expand Down

0 comments on commit 57ddf35

Please sign in to comment.