Skip to content

Commit d508d5f

Browse files
committed
Adding logging and fixing some auto-tag junk
1 parent 99b7e75 commit d508d5f

File tree

5 files changed

+119
-25
lines changed

5 files changed

+119
-25
lines changed

articles/decorators.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import functools
2+
import logging
3+
import time
4+
5+
log = logging.getLogger('articles.decorators')
6+
7+
def logtime(func):
8+
9+
@functools.wraps(func)
10+
def wrapped(*args, **kwargs):
11+
if func.__class__.__name__ == 'function':
12+
executing = '%s.%s' % (func.__module__, func.__name__)
13+
elif 'method' in func.__class__.__name__:
14+
executing = '%s.%s.%s' % (func.__module__, func.__class__.__name__, func.__name__)
15+
else:
16+
executing = str(func)
17+
18+
log.debug('Logging execution time for %s with args: %s; kwargs: %s' % (executing, args, kwargs))
19+
20+
start = time.time()
21+
res = func(*args, **kwargs)
22+
duration = time.time() - start
23+
24+
log.debug('Called %s... duration: %s seconds' % (executing, duration))
25+
return res
26+
27+
return wrapped
28+
29+
def once_per_instance(func):
30+
"""Makes it so an instance method is called at most once before saving"""
31+
32+
@functools.wraps(func)
33+
def wrapped(self, *args, **kwargs):
34+
if not hasattr(self, '__run_once_methods'):
35+
self.__run_once_methods = []
36+
37+
name = func.__name__
38+
if name in self.__run_once_methods:
39+
log.debug('Method %s has already been called for %s... not calling again.' % (name, self))
40+
return False
41+
42+
res = func(self, *args, **kwargs)
43+
44+
self.__run_once_methods.append(name)
45+
return res
46+
47+
return wrapped
48+

articles/forms.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1+
import logging
2+
13
from django import forms
24
from django.utils.translation import ugettext_lazy as _
35
from models import Article, Tag
46

7+
log = logging.getLogger('articles.forms')
8+
59
def tag(name):
610
"""Returns a Tag object for the given name"""
711

8-
t = Tag.objects.get_or_create(slug=Tag.clean_tag(name))[0]
12+
slug = Tag.clean_tag(name)
13+
14+
log.debug('Looking for Tag with slug "%s"...' % (slug,))
15+
t, created = Tag.objects.get_or_create(slug=slug, defaults={'name': name})
16+
log.debug('Found Tag %s. Name: %s Slug: %s Created: %s' % (t.pk, t.name, t.slug, created))
17+
918
if not t.name:
1019
t.name = name
1120
t.save()
@@ -31,7 +40,9 @@ def __init__(self, *args, **kwargs):
3140
def clean_tags(self):
3241
"""Turns the string of tags into a list"""
3342

34-
tags = [tag(t) for t in self.cleaned_data['tags'].split()]
43+
tags = [tag(t.strip()) for t in self.cleaned_data['tags'].split() if len(t.strip())]
44+
45+
log.debug('Tagging Article %s with: %s' % (self.cleaned_data['title'], tags))
3546
self.cleaned_data['tags'] = tags
3647
return self.cleaned_data['tags']
3748

articles/listeners.py

+29-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,34 @@
1-
from django.db.models import signals
1+
import logging
2+
3+
from django.db.models import signals, Q
4+
5+
from decorators import logtime
26
from models import Article, Tag
37

4-
def apply_new_tag(sender, instance, created, **kwargs):
5-
"""
6-
Applies new tags to existing articles that are marked for auto-tagging
7-
"""
8+
log = logging.getLogger('articles.listeners')
9+
10+
@logtime
11+
def apply_new_tag(sender, instance, created, using='default', **kwargs):
12+
"""Applies new tags to existing articles that are marked for auto-tagging"""
13+
14+
# attempt to find all articles that contain the new tag
15+
# TODO: make sure this is standard enough... seems that both MySQL and
16+
# PostgreSQL support it...
17+
tag = r'[[:<:]]%s[[:>:]]' % instance.name
18+
19+
log.debug('Searching for auto-tag Articles using regex: %s' % (tag,))
20+
applicable_articles = Article.objects.filter(
21+
Q(auto_tag=True),
22+
Q(content__iregex=tag) |
23+
Q(title__iregex=tag) |
24+
Q(description__iregex=tag) |
25+
Q(keywords__iregex=tag)
26+
)
827

9-
for article in Article.objects.filter(auto_tag=True):
10-
article.do_auto_tag()
28+
log.debug('Found %s matches' % len(applicable_articles))
29+
for article in applicable_articles:
30+
log.debug('Applying Tag "%s" (%s) to Article "%s" (%s)' % (instance, instance.pk, article.title, article.pk))
31+
article.tags.add(instance)
32+
article.save()
1133

1234
signals.post_save.connect(apply_new_tag, sender=Tag)

articles/models.py

+25-16
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
from django.template.defaultfilters import slugify, striptags
1616
from django.utils.translation import ugettext_lazy as _
1717

18+
from decorators import logtime, once_per_instance
19+
1820
WORD_LIMIT = getattr(settings, 'ARTICLES_TEASER_LIMIT', 75)
1921
AUTO_TAG = getattr(settings, 'ARTICLES_AUTO_TAG', True)
2022
DEFAULT_DB = getattr(settings, 'ARTICLES_DEFAULT_DB', 'default')
@@ -41,7 +43,7 @@
4143
TITLE_RE = re.compile('<title.*?>(.*?)</title>', re.I|re.M)
4244
TAG_RE = re.compile('[^a-z0-9\-_\+\:\.]?', re.I)
4345

44-
log = logging.getLogger(__file__)
46+
log = logging.getLogger('articles.models')
4547

4648
def get_name(user):
4749
"""
@@ -312,29 +314,36 @@ def do_meta_description(self):
312314

313315
return False
314316

317+
@logtime
318+
@once_per_instance
315319
def do_auto_tag(self, using=DEFAULT_DB):
316320
"""
317321
Performs the auto-tagging work if necessary.
318322
319323
Returns True if an additional save is required, False otherwise.
320324
"""
321325

326+
if not self.auto_tag:
327+
log.debug('Article "%s" (ID: %s) is not marked for auto-tagging. Skipping.' % (self.title, self.pk))
328+
return False
329+
330+
# don't clobber any existing tags!
331+
existing_ids = [t.id for t in self.tags.all()]
332+
log.debug('Article %s already has these tags: %s' % (self.pk, existing_ids))
333+
334+
unused = Tag.objects.all()
335+
if hasattr(unused, 'using'):
336+
unused = unused.using(using)
337+
unused = unused.exclude(id__in=existing_ids)
338+
322339
found = False
323-
if self.auto_tag:
324-
# don't clobber any existing tags!
325-
existing_ids = [t.id for t in self.tags.all()]
326-
327-
unused = Tag.objects.all()
328-
if hasattr(unused, 'using'):
329-
unused = unused.using(using)
330-
unused = unused.exclude(id__in=existing_ids)
331-
332-
for tag in unused:
333-
regex = re.compile(r'\b%s\b' % tag.name, re.I)
334-
if regex.search(self.content) or regex.search(self.title) or \
335-
regex.search(self.description) or regex.search(self.keywords):
336-
self.tags.add(tag)
337-
found = True
340+
to_search = (self.content, self.title, self.description, self.keywords)
341+
for tag in unused:
342+
regex = re.compile(r'\b%s\b' % tag.name, re.I)
343+
if any(regex.search(text) for text in to_search):
344+
log.debug('Applying Tag "%s" (%s) to Article %s' % (tag, tag.pk, self.pk))
345+
self.tags.add(tag)
346+
found = True
338347

339348
return found
340349

articles/views.py

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import logging
2+
13
from django.conf import settings
24
from django.contrib.auth.models import User
35
from django.core.cache import cache
@@ -11,6 +13,8 @@
1113

1214
ARTICLE_PAGINATION = getattr(settings, 'ARTICLE_PAGINATION', 20)
1315

16+
log = logging.getLogger('articles.views')
17+
1418
def display_blog_page(request, tag=None, username=None, year=None, month=None, page=1):
1519
"""
1620
Handles all of the magic behind the pages that list articles in any way.

0 commit comments

Comments
 (0)