Commits

Stefan Scherfke committed 5647e87

Wrote some documentation

Comments (0)

Files changed (3)

lastfm/templatetags/lastfm_widget.py

 # encoding: utf-8
 
 """
-Displays your recently listend tracks from last.fm.
+This module defines a single template tag that will return a context object
+for your Last.fm 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 = 'http://ws.audioscrobbler.com/2.0/?'
         
     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,
 
 @register.tag
 def get_lastfm_widget(parser, token):
-    """Get the last.fm widget and store it in a template variable."""
+    """Get the variable name for the widget from the template tag."""
     try:
         tagname, _as, var_name = token.split_contents()
     except ValueError:
         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)
     @mock.patch('django.template.loader.get_template')
     @mock.patch('django.template.Context')
     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()
 # encoding: utf-8
 
+"""
+The AJAX code generate by the template tag (see
+:mod:`templatetags.lastfm_widget`) doesn’t contact Last.fm 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
+Last.fm 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 Last.fm 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 Last.fm
+    * ``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 Last.fm’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 `Last.fm API
+    call <http://www.lastfm.de/api>`_
+    
+    ``get_data(data)``: Last.fm’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 Last.fm 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 = 'http://ws.audioscrobbler.com/2.0/?'
     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,
                 'default_artist_large.png'
 
     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,
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.