aboutsummaryrefslogtreecommitdiff
path: root/tagging/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'tagging/utils.py')
-rw-r--r--tagging/utils.py140
1 files changed, 140 insertions, 0 deletions
diff --git a/tagging/utils.py b/tagging/utils.py
new file mode 100644
index 0000000..af50bac
--- /dev/null
+++ b/tagging/utils.py
@@ -0,0 +1,140 @@
+import math
+import re
+import types
+
+from django.db.models.query import QuerySet
+from django.utils.encoding import force_unicode, smart_unicode
+
+# Python 2.3 compatibility
+if not hasattr(__builtins__, 'set'):
+ from sets import Set as set
+
+find_tag_re = re.compile(r'[-\w]+', re.U)
+
+def get_tag_name_list(tag_names):
+ """
+ Finds tag names in the given string and return them as a list.
+ """
+ if tag_names is not None:
+ tag_names = force_unicode(tag_names)
+ results = find_tag_re.findall(tag_names or '')
+ return results
+
+def get_tag_list(tags):
+ """
+ Utility function for accepting tag input in a flexible manner.
+
+ If a ``Tag`` object is given, it will be returned in a list as
+ its single occupant.
+
+ If given, the tag names in the following will be used to create a
+ ``Tag`` ``QuerySet``:
+
+ * A string, which may contain multiple tag names.
+ * A list or tuple of strings corresponding to tag names.
+ * A list or tuple of integers corresponding to tag ids.
+
+ If given, the following will be returned as-is:
+
+ * A list or tuple of ``Tag`` objects.
+ * A ``Tag`` ``QuerySet``.
+ """
+ from tagging.models import Tag
+ if isinstance(tags, Tag):
+ return [tags]
+ elif isinstance(tags, QuerySet) and tags.model is Tag:
+ return tags
+ elif isinstance(tags, types.StringTypes):
+ return Tag.objects.filter(name__in=get_tag_name_list(tags))
+ elif isinstance(tags, (types.ListType, types.TupleType)):
+ if len(tags) == 0:
+ return tags
+ contents = set()
+ for item in tags:
+ if isinstance(item, types.StringTypes):
+ contents.add('string')
+ elif isinstance(item, Tag):
+ contents.add('tag')
+ elif isinstance(item, (types.IntType, types.LongType)):
+ contents.add('int')
+ if len(contents) == 1:
+ if 'string' in contents:
+ return Tag.objects.filter(name__in=[smart_unicode(tag) \
+ for tag in tags])
+ elif 'tag' in contents:
+ return tags
+ elif 'int' in contents:
+ return Tag.objects.filter(id__in=tags)
+ else:
+ raise ValueError(u'If a list or tuple of tags is provided, they must all be tag names, Tag objects or Tag ids.')
+ else:
+ raise ValueError(u'The tag input given was invalid.')
+
+def get_tag(tag):
+ """
+ Utility function for accepting single tag input in a flexible
+ manner.
+
+ If a ``Tag`` object is given it will be returned as-is; if a
+ string or integer are given, they will be used to lookup the
+ appropriate ``Tag``.
+
+ If no matching tag can be found, ``None`` will be returned.
+ """
+ from tagging.models import Tag
+ if isinstance(tag, Tag):
+ return tag
+
+ try:
+ if isinstance(tag, types.StringTypes):
+ return Tag.objects.get(name=tag)
+ elif isinstance(tag, (types.IntType, types.LongType)):
+ return Tag.objects.get(id=tag)
+ except Tag.DoesNotExist:
+ pass
+
+ return None
+
+# Font size distribution algorithms
+LOGARITHMIC, LINEAR = 1, 2
+
+def calculate_cloud(tags, steps=4, distribution=LOGARITHMIC):
+ """
+ Add a ``font_size`` attribute to each tag according to the
+ frequency of its use, as indicated by its ``count``
+ attribute.
+
+ ``steps`` defines the range of font sizes - ``font_size`` will
+ be an integer between 1 and ``steps`` (inclusive).
+
+ ``distribution`` defines the type of font size distribution
+ algorithm which will be used - logarithmic or linear. It must be
+ either ``tagging.utils.LOGARITHMIC`` or ``tagging.utils.LINEAR``.
+
+ The algorithm to scale the tags logarithmically is from a
+ blog post by Anders Pearson, 'Scaling tag clouds':
+ http://thraxil.com/users/anders/posts/2005/12/13/scaling-tag-clouds/
+ """
+ if len(tags) > 0:
+ thresholds = []
+ counts = [tag.count for tag in tags]
+ max_weight = float(max(counts))
+ min_weight = float(min(counts))
+
+ # Set up the appropriate thresholds
+ if distribution == LOGARITHMIC:
+ thresholds = [math.pow(max_weight - min_weight + 1, float(i) / float(steps)) \
+ for i in range(1, steps + 1)]
+ elif distribution == LINEAR:
+ delta = (max_weight - min_weight) / float(steps)
+ thresholds = [min_weight + i * delta for i in range(1, steps + 1)]
+ else:
+ raise ValueError(u'Invalid font size distribution algorithm specified: %s.' % distribution)
+
+ for tag in tags:
+ font_set = False
+ for i in range(steps):
+ if not font_set and tag.count <= thresholds[i]:
+ tag.font_size = i + 1
+ font_set = True
+ return tags