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 From 0344f8ad564644c50203985255fab1d053aed463 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 15 Nov 2011 15:04:33 -0600 Subject: Add ability to cache users by username on the UserFinder This is very useful in the signoff message population script where we are very likely to encounter the same users over and over. Signed-off-by: Dan McGee --- devel/utils.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'devel/utils.py') diff --git a/devel/utils.py b/devel/utils.py index d7a154a8..62b12cd5 100644 --- a/devel/utils.py +++ b/devel/utils.py @@ -47,6 +47,7 @@ SELECT pr.user_id, COUNT(*), COUNT(p.flag_date) class UserFinder(object): def __init__(self): self.cache = {} + self.username_cache = {} @staticmethod def user_email(name, email): @@ -111,7 +112,22 @@ class UserFinder(object): self.cache[userstring] = user return user + def find_by_username(self, username): + if not username: + return None + if username in self.username_cache: + return self.username_cache[username] + + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + user = None + + self.username_cache[username] = user + return user + def clear_cache(self): self.cache = {} + self.username_cache = {} # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b