__title__ = 'django-qartez'
__version__ = '0.6'
__build__ = 0x000006
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__all__ = ('ImagesSitemap', 'StaticSitemap', 'RelAlternateHreflangSitemap',)
import datetime
from six import PY3
from django.contrib.sitemaps import Sitemap, GenericSitemap
from django.core.urlresolvers import reverse_lazy
from django.utils.functional import lazy
from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured
from qartez.constants import REL_ALTERNATE_HREFLANG_SITEMAP_TEMPLATE
from qartez.settings import (
PREPEND_LOC_URL_WITH_SITE_URL, PREPEND_IMAGE_LOC_URL_WITH_SITE_URL
)
PY2 = not PY3
[docs]class ImagesSitemap(GenericSitemap):
"""
Class for image sitemap. Implemented accordings to specs specifed by Google
http://www.google.com/support/webmasters/bin/answer.py?answer=178636
:example:
>>> from qartez import ImagesSitemap
>>>
>>> foo_item_images_info_dict = {
>>> 'queryset': FooItem._default_manager.exclude(image=None), # queryset
>>> 'image_location_field': 'image', # image location
>>> 'image_title_field': 'title', # image title
>>> 'location_field': 'get_absolute_url' # an absolute URL of the page
>>> # where image is shown
>>> }
>>>
>>> foo_item_images_sitemap = {
>>> 'foo_item_images': ImagesSitemap(foo_item_images_info_dict, \
>>> priority=0.6),
>>> }
"""
def __init__(self, info_dict, priority=None, changefreq=None):
"""
Constructor.
:param dict info_dict:
:param priority float:
:param str changefreq:
"""
self.image_location_field = info_dict.get('image_location_field', None)
self.image_caption_field = info_dict.get('image_caption_field', None)
self.image_title_field = info_dict.get('image_title_field', None)
if self.image_title_field:
if PY2:
self.image_title_field = unicode(self.image_title_field)
else:
self.image_title_field = str(self.image_title_field)
self.image_geo_location_field = info_dict.get(
'image_geo_location_field', None
)
self.image_license_field = info_dict.get('image_license_field', None)
self.location_field = info_dict.get('location_field', None)
super(ImagesSitemap, self).__init__(info_dict, priority, changefreq)
[docs] def image_location(self, item):
"""
Gets image location.
"""
if self.image_location_field is not None:
try:
image_location_field = getattr(item, self.image_location_field)
if callable(image_location_field):
return image_location_field()
else:
return image_location_field
except Exception as e:
return None
return None
[docs] def image_caption(self, item):
"""
Gets image caption.
"""
if self.image_caption_field is not None:
return getattr(item, self.image_caption_field)
return None
[docs] def image_title(self, item):
"""
Gets image title.
"""
if self.image_title_field is not None:
return getattr(item, self.image_title_field)
return None
[docs] def image_geo_location(self, item):
"""
Gets image geo location.
"""
if self.image_geo_location_field is not None:
return getattr(item, self.image_geo_location_field)
return None
[docs] def image_license(self, item):
"""
Gets image geo location.
"""
if self.image_license_field is not None:
return getattr(item, self.image_license_field)
return None
[docs] def location(self, item):
"""
Gets image location URL.
"""
if self.location_field is not None:
try:
location_field = getattr(item, self.location_field)
if callable(location_field):
return location_field()
else:
return location_field
except Exception as e:
return None
return item.get_absolute_url()
def __get(self, name, obj, default=None):
try:
attr = getattr(self, name)
except AttributeError:
return default
if callable(attr):
return attr(obj)
return attr
def get_urls(self, page=1, site=None, protocol=None):
# Determine protocol
if self.protocol is not None:
protocol = self.protocol
if protocol is None:
protocol = 'http'
# Determine domain
if site is None:
if Site._meta.installed:
try:
site = Site.objects.get_current()
except Site.DoesNotExist:
pass
if site is None:
raise ImproperlyConfigured(
"To use sitemaps, either enable the sites framework or "
"pass a Site/RequestSite object in your view."
)
domain = site.domain
urls = []
for item in self.paginator.page(page).object_list:
loc = self.__get('location', item, None)
if loc and PREPEND_LOC_URL_WITH_SITE_URL:
if PY2:
loc = "{0}://{1}{2}".format(
protocol, unicode(domain), unicode(loc)
)
else:
loc = "{0}://{1}{2}".format(protocol, str(domain), str(loc))
image_loc = self.__get('image_location', item, None)
if image_loc and PREPEND_IMAGE_LOC_URL_WITH_SITE_URL:
try:
if PY2:
image_loc = "{0}://{1}{2}".format(
protocol, unicode(domain), unicode(image_loc)
)
else:
image_loc = "{0}://{1}{2}".format(
protocol, str(domain), str(image_loc)
)
except Exception as e:
continue
# Validating the changefreq
changefreq = self.__get('changefreq', item, None)
if changefreq is not None:
assert changefreq in CHANGEFREQ
# Validating the priority
priority = self.__get('priority', item, None)
if priority is not None:
assert priority >= 0 and priority <= 1
url_info = {
'location': loc,
'image_location': image_loc,
'image_caption': self.__get('image_caption', item, None),
'image_title': self.__get('image_title', item, None),
'image_license': self.__get('image_license', item, None),
'image_geo_location': self.__get(
'image_geo_location', item, None
),
'lastmod': self.__get('lastmod', item, None),
'changefreq': changefreq,
'priority': priority
}
urls.append(url_info)
return urls
[docs]class StaticSitemap(Sitemap):
"""
Sitemap for ``static`` pages. See constructor docstring for list of
accepted (additional) arguments.
:example:
>>> from qartez import StaticSitemap
>>> service_sitemap = StaticSitemap(priority=0.1, changefreq='never')
>>> service_sitemap.add_named_pattern('blog.welcome')
>>> service_sitemap.add_named_pattern('feedback.contact')
>>>
>>> content_types_sitemap = StaticSitemap(priority=1.0, changefreq='daily')
>>> content_types_sitemap.add_named_pattern('blog.browse') # Homepage
>>> content_types_sitemap.add_named_pattern(
>>> 'blog.browse', kwargs={'content_type': 'articles'}
>>> ) # Articles
>>> content_types_sitemap.add_named_pattern(
>>> 'blog.browse', kwargs={'content_type': 'downloads'}
>>> ) # Downloads
"""
NAMED_PATTERN = 1
URL = 2
def __init__(self, *args, **kwargs):
"""
Constructor. Accepts the following optional keyword-arguments (to
be only specified as keyword-arguments).
:param float priority:
:param str changefreq:
:param datetime.datetime|str lastmod:
"""
if 'priority' in kwargs:
self.priority = kwargs.pop('priority')
else:
self.priority = 1.0
if 'changefreq' in kwargs:
self.changefreq = kwargs.pop('changefreq')
else:
self.changefreq = 'never'
if 'lastmod' in kwargs:
self.lastmod = kwargs.pop('lastmod')
else:
self.lastmod = datetime.datetime.now()
super(StaticSitemap, self).__init__(*args, **kwargs)
self._items = []
[docs] def items(self):
"""
Returns sitemap items.
:return list:
"""
return self._items
def location(self, obj):
return obj['location']
[docs] def add_named_pattern(self, viewname, urlconf=None, args=[], kwargs=None, \
lastmod=None, changefreq=None, priority=None):
"""
Ads a named pattern to the items list.
:param str viewname:
:param urlconf:
:param list args:
:param dict kwargs:
:param lastmod:
:param str changefreq:
:param float priority:
"""
try:
loc = reverse_lazy(viewname, urlconf, args, kwargs)
self._items.append({
'location': loc,
'lastmod': lastmod or self.lastmod,
'changefreq': changefreq if changefreq else self.changefreq,
'priority': priority if priority else self.priority
})
except Exception as e:
pass
[docs] def add_url(self, url, lastmod=None, changefreq=None, priority=None):
"""
Adds a URL to the items list.
:param str url:
:param lastmod:
:param str changefreq:
:param float priority:
"""
try:
self.items.append({
'location': url,
'lastmod': lastmod or self.lastmod,
'changefreq': changefreq if changefreq else self.changefreq,
'priority': priority if priority else self.priority
})
except Exception as e:
pass
[docs] def get_urls(self, *args, **kwargs):
"""
Make sure nothing breaks if some URL is unresolvable.
:return list:
"""
try:
return super(StaticSitemap, self).get_urls(*args, **kwargs)
except Exception as e:
return []
[docs]class RelAlternateHreflangSitemap(Sitemap):
"""
Sitemaps: rel="alternate" hreflang="x" implementation.
Read the specs the specs here
http://support.google.com/webmasters/bin/answer.py?hl=en&answer=2620865
IMPORTANT: When you use this class you have to override
the ``alternate_hreflangs`` method in your sitemap class.
:example:
>>> from qartez import RelAlternateHreflangSitemap
>>>
>>> class ArticleSitemap(RelAlternateHreflangSitemap):
>>> def alternate_hreflangs(self, obj):
>>> return [('en-us', obj.alternative_object_url),]
"""
def __get(self, name, obj, default=None):
try:
attr = getattr(self, name)
except AttributeError:
return default
if callable(attr):
return attr(obj)
return attr
[docs] def alternate_hreflangs(self, item):
"""
You should override the "alternate_hreflangs" method in your sitemap
class.
"""
raise NotImplementedError(
"""You have to override the "alternate_hreflangs" method in """
"""your sitemap class. Refer to "qartez" app documentation for """
"""details and examples."""
)
def _render_alternate_hreflangs(self, item):
"""
Renders the tiny bit of XML responsible for rendering the alternate
hreflang code.
:return str:
"""
alternate_hreflangs = self.__get('alternate_hreflangs', item, [])
output = ""
if alternate_hreflangs:
for hreflang in alternate_hreflangs:
output += REL_ALTERNATE_HREFLANG_SITEMAP_TEMPLATE.format(
**{'lang': hreflang[0], 'href': hreflang[1]}
)
return output
def get_urls(self, page=1, site=None, protocol=None):
# Determine protocol
if self.protocol is not None:
protocol = self.protocol
if protocol is None:
protocol = 'http'
# Determine domain
if site is None:
if Site._meta.installed:
try:
site = Site.objects.get_current()
except Site.DoesNotExist:
pass
if site is None:
raise ImproperlyConfigured(
"To use sitemaps, either enable the sites framework "
"or pass a Site/RequestSite object in your view."
)
domain = site.domain
urls = []
for item in self.paginator.page(page).object_list:
loc = "{0}://{1}{2}".format(
protocol, domain, self.__get('location', item)
)
url_info = {
'location': loc,
'lastmod': self.__get('lastmod', item, None),
'changefreq': self.__get('changefreq', item, None),
'priority': self.__get('priority', item, None),
'alternate_hreflangs': self._render_alternate_hreflangs(item),
}
urls.append(url_info)
return urls