From da20949c8cc185e91dbaae1b8369fcffa3447081 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 23 Jun 2011 19:11:12 -0500 Subject: Move find_user method to devel utils This could be handy elsewhere as well, and it is loosely coupled to anything else in reporead. Signed-off-by: Dan McGee --- devel/utils.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'devel/utils.py') diff --git a/devel/utils.py b/devel/utils.py index abfdabe5..9d7dfb2d 100644 --- a/devel/utils.py +++ b/devel/utils.py @@ -1,7 +1,11 @@ +import re + from django.contrib.auth.models import User from django.db import connection +from django.db.models import Count, Q from main.utils import cache_function +from main.models import Package from packages.models import PackageRelation @cache_function(300) @@ -28,10 +32,64 @@ SELECT pr.user_id, COUNT(*), COUNT(p.flag_date) pkg_count[k] = total flag_count[k] = flagged + update_count = Package.objects.values_list('packager').annotate( + Count('packager')) + update_count = dict(update_count) + for m in maintainers: m.package_count = pkg_count.get(m.id, 0) m.flagged_count = flag_count.get(m.id, 0) + m.updated_count = update_count.get(m.id, 0) return maintainers +def find_user(userstring): + ''' + Attempt to find the corresponding User object for a standard + packager string, e.g. something like + 'A. U. Thor '. + We start by searching for a matching email address; we then move onto + matching by first/last name. If we cannot find a user, then return None. + ''' + if userstring in find_user.cache: + return find_user.cache[userstring] + matches = re.match(r'^([^<]+)? ?<([^>]*)>', userstring) + if not matches: + return None + + user = None + name = matches.group(1) + email = matches.group(2) + + def user_email(): + return User.objects.get(email=email) + def profile_email(): + return User.objects.get(userprofile__public_email=email) + def user_name(): + # yes, a bit odd but this is the easiest way since we can't always be + # sure how to split the name. Ensure every 'token' appears in at least + # one of the two name fields. + name_q = Q() + for token in name.split(): + # ignore quoted parts; e.g. nicknames in strings + if re.match(r'^[\'"].*[\'"]$', token): + continue + name_q &= (Q(first_name__icontains=token) | + Q(last_name__icontains=token)) + return User.objects.get(name_q) + + for matcher in (user_email, profile_email, user_name): + try: + user = matcher() + break + except (User.DoesNotExist, User.MultipleObjectsReturned): + pass + + find_user.cache[userstring] = user + return user + +# cached mappings of user strings -> User objects so we don't have to do the +# lookup more than strictly necessary. +find_user.cache = {} + # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From 26c54d017185b1c409dbd6ed4c09fb14986df0b3 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 23 Jun 2011 19:35:47 -0500 Subject: find_user: add tests and fix no email address case If a packager string was passed in without an email address, we would blow up on the matcher and not try to find a user. Signed-off-by: Dan McGee --- devel/utils.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'devel/utils.py') diff --git a/devel/utils.py b/devel/utils.py index 9d7dfb2d..acdda959 100644 --- a/devel/utils.py +++ b/devel/utils.py @@ -51,24 +51,32 @@ def find_user(userstring): We start by searching for a matching email address; we then move onto matching by first/last name. If we cannot find a user, then return None. ''' + if not userstring: + return None if userstring in find_user.cache: return find_user.cache[userstring] matches = re.match(r'^([^<]+)? ?<([^>]*)>', userstring) if not matches: - return None - - user = None - name = matches.group(1) - email = matches.group(2) + name = userstring + email = None + else: + name = matches.group(1) + email = matches.group(2) def user_email(): - return User.objects.get(email=email) + if email: + return User.objects.get(email=email) + return None def profile_email(): - return User.objects.get(userprofile__public_email=email) + if email: + return User.objects.get(userprofile__public_email=email) + return None def user_name(): # yes, a bit odd but this is the easiest way since we can't always be # sure how to split the name. Ensure every 'token' appears in at least # one of the two name fields. + if not name: + return None name_q = Q() for token in name.split(): # ignore quoted parts; e.g. nicknames in strings @@ -78,10 +86,12 @@ def find_user(userstring): Q(last_name__icontains=token)) return User.objects.get(name_q) + user = None for matcher in (user_email, profile_email, user_name): try: user = matcher() - break + if user != None: + break except (User.DoesNotExist, User.MultipleObjectsReturned): pass -- cgit v1.2.3-2-g168b From 9156003d2d93de57c663901c39ac66316a3d969e Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 23 Jun 2011 19:50:46 -0500 Subject: Turn find_user into UserFinder class This moves the cache inside an instance. Also add a few more tests. Signed-off-by: Dan McGee --- devel/utils.py | 86 ++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 38 deletions(-) (limited to 'devel/utils.py') diff --git a/devel/utils.py b/devel/utils.py index acdda959..3a6ad699 100644 --- a/devel/utils.py +++ b/devel/utils.py @@ -43,35 +43,25 @@ SELECT pr.user_id, COUNT(*), COUNT(p.flag_date) return maintainers -def find_user(userstring): - ''' - Attempt to find the corresponding User object for a standard - packager string, e.g. something like - 'A. U. Thor '. - We start by searching for a matching email address; we then move onto - matching by first/last name. If we cannot find a user, then return None. - ''' - if not userstring: - return None - if userstring in find_user.cache: - return find_user.cache[userstring] - matches = re.match(r'^([^<]+)? ?<([^>]*)>', userstring) - if not matches: - name = userstring - email = None - else: - name = matches.group(1) - email = matches.group(2) - - def user_email(): + +class UserFinder(object): + def __init__(self): + self.cache = {} + + @staticmethod + def user_email(name, email): if email: return User.objects.get(email=email) return None - def profile_email(): + + @staticmethod + def profile_email(name, email): if email: return User.objects.get(userprofile__public_email=email) return None - def user_name(): + + @staticmethod + def user_name(name, email): # yes, a bit odd but this is the easiest way since we can't always be # sure how to split the name. Ensure every 'token' appears in at least # one of the two name fields. @@ -86,20 +76,40 @@ def find_user(userstring): Q(last_name__icontains=token)) return User.objects.get(name_q) - user = None - for matcher in (user_email, profile_email, user_name): - try: - user = matcher() - if user != None: - break - except (User.DoesNotExist, User.MultipleObjectsReturned): - pass - - find_user.cache[userstring] = user - return user - -# cached mappings of user strings -> User objects so we don't have to do the -# lookup more than strictly necessary. -find_user.cache = {} + def find(self, userstring): + ''' + Attempt to find the corresponding User object for a standard + packager string, e.g. something like + 'A. U. Thor '. + We start by searching for a matching email address; we then move onto + matching by first/last name. If we cannot find a user, then return None. + ''' + if not userstring: + return None + if userstring in self.cache: + return self.cache[userstring] + matches = re.match(r'^([^<]+)? ?<([^>]*)>', userstring) + if not matches: + name = userstring + email = None + else: + name = matches.group(1) + email = matches.group(2) + + user = None + find_methods = (self.user_email, self.profile_email, self.user_name) + for matcher in find_methods: + try: + user = matcher(name, email) + if user != None: + break + except (User.DoesNotExist, User.MultipleObjectsReturned): + pass + + self.cache[userstring] = user + return user + + def clear_cache(self): + self.cache = {} # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From 82289ebb4432b3372b959430581afa0a2158acb9 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 23 Jun 2011 20:11:07 -0500 Subject: Add a rematch_packager management command This allows quick resolution of all unmatched packages, especially after tweaking the way find_user works. Signed-off-by: Dan McGee --- devel/utils.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'devel/utils.py') diff --git a/devel/utils.py b/devel/utils.py index 3a6ad699..6bc52c89 100644 --- a/devel/utils.py +++ b/devel/utils.py @@ -88,10 +88,12 @@ class UserFinder(object): return None if userstring in self.cache: return self.cache[userstring] - matches = re.match(r'^([^<]+)? ?<([^>]*)>', userstring) + + name = email = None + + matches = re.match(r'^([^<]+)? ?<([^>]*)>?', userstring) if not matches: - name = userstring - email = None + name = userstring.strip() else: name = matches.group(1) email = matches.group(2) -- cgit v1.2.3-2-g168b From f913bbcab4dc458d1566778a094bdd337cb91841 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 28 Jun 2011 00:11:49 -0500 Subject: Add order_by to packager count query No real idea why SQLite is returning wrong results without out this, but it is likely a bug in the ORM layer I'm not interested in digging into. Signed-off-by: Dan McGee --- devel/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'devel/utils.py') diff --git a/devel/utils.py b/devel/utils.py index 6bc52c89..d7a154a8 100644 --- a/devel/utils.py +++ b/devel/utils.py @@ -32,8 +32,8 @@ SELECT pr.user_id, COUNT(*), COUNT(p.flag_date) pkg_count[k] = total flag_count[k] = flagged - update_count = Package.objects.values_list('packager').annotate( - Count('packager')) + update_count = Package.objects.values_list('packager').order_by( + 'packager').annotate(Count('packager')) update_count = dict(update_count) for m in maintainers: -- cgit v1.2.3-2-g168b