From d685d51e8c80f29d4f14977a0d8088534e6626c4 Mon Sep 17 00:00:00 2001
From: Dan McGee <dan@archlinux.org>
Date: Sun, 20 May 2012 13:21:59 -0500
Subject: Ensure we use last_modified date from News in headers

We were actually using the postdate attribute rather than last_modified,
which means any News objects that get edited would not trigger an update
of the feed.

Signed-off-by: Dan McGee <dan@archlinux.org>
---
 main/utils.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

(limited to 'main/utils.py')

diff --git a/main/utils.py b/main/utils.py
index e7e47c53..b7cb19f4 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -72,7 +72,7 @@ def refresh_latest(**kwargs):
     cache.set(cache_key, None, INVALIDATE_TIMEOUT)
 
 
-def retrieve_latest(sender):
+def retrieve_latest(sender, latest_by=None):
     # we could break this down based on the request url, but it would probably
     # cost us more in query time to do so.
     cache_key = CACHE_LATEST_PREFIX + sender.__name__
@@ -80,8 +80,9 @@ def retrieve_latest(sender):
     if latest:
         return latest
     try:
-        latest_by = sender._meta.get_latest_by
-        latest = sender.objects.values(latest_by).latest()[latest_by]
+        if latest_by is None:
+            latest_by = sender._meta.get_latest_by
+        latest = sender.objects.values(latest_by).latest(latest_by)[latest_by]
         # Using add means "don't overwrite anything in there". What could be in
         # there is an explicit None value that our refresh signal set, which
         # means we want to avoid race condition possibilities for a bit.
-- 
cgit v1.2.3-2-g168b


From 26a00cadcebc0b37775954d261ec73f927ceca12 Mon Sep 17 00:00:00 2001
From: Dan McGee <dan@archlinux.org>
Date: Sat, 7 Jul 2012 17:28:02 -0500
Subject: Don't log package updates in Python when we have DB trigger support

This adds a helper method to find the database engine in use, and then
skips code we shouldn't execute if we are doing this another way.

Note that this helper method could be useful for backend-specific code
paths elsewhere, such as custom SQL being called or lack of StdDev() in
sqlite3 out of the box.

Signed-off-by: Dan McGee <dan@archlinux.org>
---
 main/utils.py | 11 +++++++++++
 1 file changed, 11 insertions(+)

(limited to 'main/utils.py')

diff --git a/main/utils.py b/main/utils.py
index b7cb19f4..879abfb9 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -8,6 +8,7 @@ import hashlib
 from pytz import utc
 
 from django.core.cache import cache
+from django.db import connections, router
 
 
 CACHE_TIMEOUT = 1800
@@ -106,6 +107,16 @@ def set_created_field(sender, **kwargs):
         obj.created = utc_now()
 
 
+def database_vendor(model, mode='read'):
+    if mode == 'read':
+        database = router.db_for_read(model)
+    elif mode == 'write':
+        database = router.db_for_write(model)
+    else:
+        raise Exception('Invalid database mode specified')
+    return connections[database].vendor
+
+
 def groupby_preserve_order(iterable, keyfunc):
     '''Take an iterable and regroup using keyfunc to determine whether items
     belong to the same group. The order of the iterable is preserved and
-- 
cgit v1.2.3-2-g168b


From c0bf9e20660cfae7ea8994472555bba23398b598 Mon Sep 17 00:00:00 2001
From: Dan McGee <dan@archlinux.org>
Date: Tue, 24 Jul 2012 09:19:48 -0500
Subject: Remove custom utc_now() function, use django.utils.timezone.now()

This was around from the time when we handled timezones sanely and
Django did not; now that we are on 1.4 we no longer need our own code to
handle this.

Signed-off-by: Dan McGee <dan@archlinux.org>
---
 main/utils.py | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

(limited to 'main/utils.py')

diff --git a/main/utils.py b/main/utils.py
index 879abfb9..0b6849a4 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -5,10 +5,10 @@ except ImportError:
 
 from datetime import datetime
 import hashlib
-from pytz import utc
 
 from django.core.cache import cache
 from django.db import connections, router
+from django.utils.timezone import now
 
 
 CACHE_TIMEOUT = 1800
@@ -94,17 +94,12 @@ def retrieve_latest(sender, latest_by=None):
     return None
 
 
-def utc_now():
-    '''Returns a timezone-aware UTC date representing now.'''
-    return datetime.utcnow().replace(tzinfo=utc)
-
-
 def set_created_field(sender, **kwargs):
     '''This will set the 'created' field on any object to the current UTC time
     if it is unset. For use as a pre_save signal handler.'''
     obj = kwargs['instance']
     if hasattr(obj, 'created') and not obj.created:
-        obj.created = utc_now()
+        obj.created = now()
 
 
 def database_vendor(model, mode='read'):
-- 
cgit v1.2.3-2-g168b


From 5df83a38282d8ddbc653859914eb49cad1c30494 Mon Sep 17 00:00:00 2001
From: Dan McGee <dan@archlinux.org>
Date: Tue, 4 Sep 2012 08:53:39 -0500
Subject: Add a 'format_http_headers' method

This takes a HttpRequest object and grabs the HTTP headers out of it and
pretty-prints them in a familiar format. This will come in handy if we
want to log these when creating package FlagRequests, releng Tests, etc.
in addition to already logging the IP address of the user posting the
request.

Signed-off-by: Dan McGee <dan@archlinux.org>
---
 main/utils.py | 10 ++++++++++
 1 file changed, 10 insertions(+)

(limited to 'main/utils.py')

diff --git a/main/utils.py b/main/utils.py
index 0b6849a4..d12e5e1a 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -53,6 +53,16 @@ def clear_cache_function(func, args, kwargs):
     key = cache_function_key(func, args, kwargs)
     cache.delete(key)
 
+
+def format_http_headers(request):
+    headers = sorted((k, v) for k, v in request.META.items()
+            if k.startswith('HTTP_'))
+    data = []
+    for k, v in headers:
+        data.extend([k[5:].replace('_', '-').title(), ': ', v, '\n'])
+    return ''.join(data)
+
+
 # utility to make a pair of django choices
 make_choice = lambda l: [(str(m), str(m)) for m in l]
 
-- 
cgit v1.2.3-2-g168b


From bf4385a26c1b6f07bf9bdcddf7160b5eb4a71d9a Mon Sep 17 00:00:00 2001
From: Dan McGee <dan@archlinux.org>
Date: Thu, 27 Dec 2012 21:13:56 -0600
Subject: Move the body of set_last_modified to main/utils

Instead of having multiple methods, move this into our single 'created'
setter method. If the 'last_modified' property is present, we now update
it accordingly when saving any model with this signal attached.

Signed-off-by: Dan McGee <dan@archlinux.org>
---
 main/utils.py | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

(limited to 'main/utils.py')

diff --git a/main/utils.py b/main/utils.py
index d12e5e1a..17ca386e 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -106,10 +106,16 @@ def retrieve_latest(sender, latest_by=None):
 
 def set_created_field(sender, **kwargs):
     '''This will set the 'created' field on any object to the current UTC time
-    if it is unset. For use as a pre_save signal handler.'''
+    if it is unset.
+    Additionally, this will set the 'last_modified' field on any object to the
+    current UTC time on any save of the object.
+    For use as a pre_save signal handler.'''
     obj = kwargs['instance']
+    time = now()
     if hasattr(obj, 'created') and not obj.created:
-        obj.created = now()
+        obj.created = time
+    if hasattr(obj, 'last_modified'):
+        obj.last_modified = time
 
 
 def database_vendor(model, mode='read'):
-- 
cgit v1.2.3-2-g168b


From 563a618e697c918c2a76c63a5217047a8d3c1489 Mon Sep 17 00:00:00 2001
From: Dan McGee <dan@archlinux.org>
Date: Fri, 28 Dec 2012 10:12:09 -0600
Subject: Move slug creation helper to main/utils

Signed-off-by: Dan McGee <dan@archlinux.org>
---
 main/utils.py | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

(limited to 'main/utils.py')

diff --git a/main/utils.py b/main/utils.py
index 17ca386e..cdd4ff71 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -9,6 +9,7 @@ import hashlib
 from django.core.cache import cache
 from django.db import connections, router
 from django.utils.timezone import now
+from django.template.defaultfilters import slugify
 
 
 CACHE_TIMEOUT = 1800
@@ -118,6 +119,20 @@ def set_created_field(sender, **kwargs):
         obj.last_modified = time
 
 
+def find_unique_slug(model, title):
+    '''Attempt to find a unique slug for this model with given title.'''
+    existing = set(model.objects.values_list(
+        'slug', flat=True).order_by().distinct())
+
+    suffixed = slug = slugify(title)
+    suffix = 0
+    while suffixed in existing:
+        suffix += 1
+        suffixed = "%s-%d" % (slug, suffix)
+
+    return suffixed
+
+
 def database_vendor(model, mode='read'):
     if mode == 'read':
         database = router.db_for_read(model)
-- 
cgit v1.2.3-2-g168b


From 31d39e75eea7fb6cdf3bb8bfd8b490d45de04ee9 Mon Sep 17 00:00:00 2001
From: Dan McGee <dan@archlinux.org>
Date: Tue, 16 Apr 2013 21:59:32 -0500
Subject: Add shortcut for HEAD requests on slower views

We sometimes see some web bots and crawlers make HEAD requests to verify
existence of certain pages in the application. However, they are less
than kind as 20-50 requests might arrive at the same time, and package
search and details pages are some of the slowest rendering pages we have
due to the Django template engine.

Rather than waste time generating the content only to throw it away,
response as soon as we can with either a 404 or 200 response as
appropriate, omitting the 'Content-Length' header completely, which
seems to be acceptable by the HTTP spec.

Signed-off-by: Dan McGee <dan@archlinux.org>
---
 main/utils.py | 9 +++++++++
 1 file changed, 9 insertions(+)

(limited to 'main/utils.py')

diff --git a/main/utils.py b/main/utils.py
index cdd4ff71..8394e5cd 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -8,6 +8,7 @@ import hashlib
 
 from django.core.cache import cache
 from django.db import connections, router
+from django.http import HttpResponse
 from django.utils.timezone import now
 from django.template.defaultfilters import slugify
 
@@ -55,6 +56,14 @@ def clear_cache_function(func, args, kwargs):
     cache.delete(key)
 
 
+def empty_response():
+    empty = HttpResponse('')
+    # designating response as 'streaming' forces ConditionalGetMiddleware to
+    # not add a 'Content-Length: 0' header
+    empty.streaming = True
+    return empty
+
+
 def format_http_headers(request):
     headers = sorted((k, v) for k, v in request.META.items()
             if k.startswith('HTTP_'))
-- 
cgit v1.2.3-2-g168b


From b7b24740640e24883cd17fd683e1d465fbb343f8 Mon Sep 17 00:00:00 2001
From: Dan McGee <dan@archlinux.org>
Date: Tue, 16 Apr 2013 22:12:01 -0500
Subject: Various minor code cleanups and fixes

Most of these were suggested by PyCharm, and include everything from
little syntax issues and other bad smells to dead or bad code.

Signed-off-by: Dan McGee <dan@archlinux.org>
---
 main/utils.py | 1 -
 1 file changed, 1 deletion(-)

(limited to 'main/utils.py')

diff --git a/main/utils.py b/main/utils.py
index 8394e5cd..9ee8db58 100644
--- a/main/utils.py
+++ b/main/utils.py
@@ -3,7 +3,6 @@ try:
 except ImportError:
     import pickle
 
-from datetime import datetime
 import hashlib
 
 from django.core.cache import cache
-- 
cgit v1.2.3-2-g168b