Stefan Scherfke committed 5647e87

Wrote some documentation

  • Participants
  • Parent commits 4f4a47c

Comments (0)

Files changed (3)

File lastfm/templatetags/

 # encoding: utf-8
-Displays your recently listend tracks from
+This module defines a single template tag that will return a context object
+for your widget. It contains a title for the widget and a ``<div>`` container. This container includes the AJAX code that retrieves the data
+from the ``lastfm`` view and generates an image for each item.
+.. sourcecode:: html+django
     {% load lastfm_widget %}
     {% get_lastfm_widget as lastfm_widget %}
+    <h2>{{ lastfm_widget.title }}</h2>
+    {{ lastfm_widget.content }}
+The generated code will roughly look like this:
+.. sourcecode:: html
+    <div>
+        <div><a><img /></a></div>
+        <div><a><img /></a></div>
+        <!-- ... -->
+    </div>
+The surrounding ``<div>`` has the CSS class *lastfm*. You can use this to
+customize the style of the widget. This could look like this:
+.. sourcecode:: css
+    #sidebar > #lastfm {
+        min-height: 225px; /* required due to "float: left" in the next sec. */
+    }
+    #sidebar #lastfm div {
+        width: 54px;
+        height: 39px;
+        overflow: hidden;
+        float: left;
+        border: 1px solid white;
+        -moz-border-radius: 2px;
+        -khtml-border-radius: 2px;
+        border-radius: 2px;
+        margin: 0px 2px 4px 2px;
+    }
+    #sidebar #lastfm div:active, #sidebar #lastfm div:hover {
+        border-color: #9FC765;
+    }
+    #sidebar #lastfm img {
+        width: 54px;
+        min-height: 39px;
+    }
 class LastfmWidgetNode(template.Node):
-    """Renders the weblog’s sidebar widgets."""
+    """This class will create a context object named ``var_name``. It will 
+    contain the ``title`` and the ``content`` of the widget."""
     def __init__(self, var_name):
         self.var_name = var_name
-        # self.chart_types = {
-        #     'recent_tracks': self._get_recent_tracks,
-        #     'weekly_top_artists': self._get_weekly_top_artists,
-        #     'top_artists': self._get_top_artists,
-        # }
-        # self.url = ''
     def render(self, context):
-        # try:
-            # data = self.chart_types[settings.LASTFM_CHART_TYPE]()
-        # except:
-        #     data = []
+        """Load the template and render it into a context variable."""
         t = template.loader.get_template('lastfm_widget/_widget.html')
         lastfm_widget = {
             'title': settings.LASTFM_WIDGET_TITLE,
 def get_lastfm_widget(parser, token):
-    """Get the widget and store it in a template variable."""
+    """Get the variable name for the widget from the template tag."""
         tagname, _as, var_name = token.split_contents()
     except ValueError:

File lastfm/

         self.token = mock.Mock(spec=['split_contents'])
     def test_get_sidebar_widgets(self):
+        """Test if ``lastfm_widget.get_lastfm_widget()`` returns a node with
+        the correct variable name."""
         tag = 'get_lastfm_widget'
         var_name = 'widgets'
         self.token.split_contents.return_value = (tag, 'as', var_name)
     def test_lastfm_widget_node(self, get_template, Context):
+        """Test if the template node contains the correct template variables."""
         class ContextMock(dict):
             autoescape = object()
         context = ContextMock()

File lastfm/

 # encoding: utf-8
+The AJAX code generate by the template tag (see
+:mod:`templatetags.lastfm_widget`) doesn’t contact directly, but uses a
+Django view as proxy. The advantage of this is, that you can exactly control
+what data your site gets. Another advantage is, that visitors can’t see your username by inspecting the HTML source of your site.
+In addition to the view itself this module also defines some helper classes that
+are responsible for handling the different types of charts (e.g. top tracks or
+top artists).
 import urllib
 from django.conf import settings
 def lastfm_data(request):
+    """This view retrievs the data from and returns a JSON encoded list.         
+    The AJAX code from the template tag will retrive this list and generate
+    the chart from it.
+    Each list entry is a with three elements:
+    * ``title``: Contains the song title or artist name and can be displayed as
+      alternative text or link title
+    * ``url``: A url pointing to the track or artist on
+    * ``img_url``: A url pointing to the track’s cover or artist image
+    For each chart type there is a class that handles its data. They are
+    necessary to unify’s different key names to those three explained
+    above. Currently, there is no abstract base class which they must inherit
+    from, but each class is expected to implement the following attributes and
+    methods:
+    ``params``: A dict that contains all parameters required by the ` API
+    call <>`_
+    ``get_data(data)``:’s JSON data is quite nested. This method should
+    extract the list with the actual items from the raw data.
+    ``get_item_title(item)``: Return an item’s text for the ``title`` attribute
+    in the data dict.
+    ``get_default_image()``: Return a url to a default image that will be used
+    if a track or artist has no image on its own.
+    ``get_img_url(url, img_size, item)``: This method needs to be implemented
+    only if an item has no ``image`` key (which is the case for e.g. the weekly
+    top artist). It gets the API url, the desired image size (small,
+    medium, large, …) and the item. It might do another API call and extract a
+    custom image URL for that item."""
     url = ''
     chart_types = {
         'recent_tracks': RecentTracks,
 class RecentTracks(object):
+    """This class handles the API call ``user.getRecentTracks``."""
     params = {
         'method': 'user.getRecentTracks',
         'user': settings.LASTFM_USER,
 class WeeklyTopArtists(object):
+    """This class handles the API call ``user.getWeeklyArtistChart``."""
     params = {
         'method': 'user.getWeeklyArtistChart',
         'user': settings.LASTFM_USER,
     def get_img_url(self, url, img_size, item):
+        """A chart item of this class does not contain any images, so we need to 
+        do another API call to get an image for artist in ``item``."""
         params = urllib.urlencode({
             'method': 'artist.getimages',
             'artist': item['name'],
 class TopArtists(object):
+    """This class handles the API call ``user.getTopArtists``. The period must 
+    be defined in the site’s settings module."""
     params = {
         'method': 'user.getTopArtists',
         'user': settings.LASTFM_USER,