faset over fra Z3950 til google books
This commit is contained in:
223
python/gdata/analytics/__init__.py
Normal file
223
python/gdata/analytics/__init__.py
Normal file
@@ -0,0 +1,223 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Original Copyright (C) 2006 Google Inc.
|
||||
# Refactored in 2009 to work for Google Analytics by Sal Uryasev at Juice Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Note that this module will not function without specifically adding
|
||||
# 'analytics': [ #Google Analytics
|
||||
# 'https://www.google.com/analytics/feeds/'],
|
||||
# to CLIENT_LOGIN_SCOPES in the gdata/service.py file
|
||||
|
||||
"""Contains extensions to Atom objects used with Google Analytics."""
|
||||
|
||||
__author__ = 'api.suryasev (Sal Uryasev)'
|
||||
|
||||
import atom
|
||||
import gdata
|
||||
|
||||
GAN_NAMESPACE = 'http://schemas.google.com/analytics/2009'
|
||||
|
||||
class TableId(gdata.GDataEntry):
|
||||
"""tableId element."""
|
||||
_tag = 'tableId'
|
||||
_namespace = GAN_NAMESPACE
|
||||
|
||||
class Property(gdata.GDataEntry):
|
||||
_tag = 'property'
|
||||
_namespace = GAN_NAMESPACE
|
||||
_children = gdata.GDataEntry._children.copy()
|
||||
_attributes = gdata.GDataEntry._attributes.copy()
|
||||
|
||||
_attributes['name'] = 'name'
|
||||
_attributes['value'] = 'value'
|
||||
|
||||
def __init__(self, name=None, value=None, *args, **kwargs):
|
||||
self.name = name
|
||||
self.value = value
|
||||
super(Property, self).__init__(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
def __repr__(self):
|
||||
return self.value
|
||||
|
||||
class AccountListEntry(gdata.GDataEntry):
|
||||
"""The Google Documents version of an Atom Entry"""
|
||||
|
||||
_tag = 'entry'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.GDataEntry._children.copy()
|
||||
_attributes = gdata.GDataEntry._attributes.copy()
|
||||
_children['{%s}tableId' % GAN_NAMESPACE] = ('tableId',
|
||||
[TableId])
|
||||
_children['{%s}property' % GAN_NAMESPACE] = ('property',
|
||||
[Property])
|
||||
|
||||
def __init__(self, tableId=None, property=None,
|
||||
*args, **kwargs):
|
||||
self.tableId = tableId
|
||||
self.property = property
|
||||
super(AccountListEntry, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def AccountListEntryFromString(xml_string):
|
||||
"""Converts an XML string into an AccountListEntry object.
|
||||
|
||||
Args:
|
||||
xml_string: string The XML describing a Document List feed entry.
|
||||
|
||||
Returns:
|
||||
A AccountListEntry object corresponding to the given XML.
|
||||
"""
|
||||
return atom.CreateClassFromXMLString(AccountListEntry, xml_string)
|
||||
|
||||
|
||||
class AccountListFeed(gdata.GDataFeed):
|
||||
"""A feed containing a list of Google Documents Items"""
|
||||
|
||||
_tag = 'feed'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.GDataFeed._children.copy()
|
||||
_attributes = gdata.GDataFeed._attributes.copy()
|
||||
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry',
|
||||
[AccountListEntry])
|
||||
|
||||
|
||||
def AccountListFeedFromString(xml_string):
|
||||
"""Converts an XML string into an AccountListFeed object.
|
||||
|
||||
Args:
|
||||
xml_string: string The XML describing an AccountList feed.
|
||||
|
||||
Returns:
|
||||
An AccountListFeed object corresponding to the given XML.
|
||||
All properties are also linked to with a direct reference
|
||||
from each entry object for convenience. (e.g. entry.AccountName)
|
||||
"""
|
||||
feed = atom.CreateClassFromXMLString(AccountListFeed, xml_string)
|
||||
for entry in feed.entry:
|
||||
for pro in entry.property:
|
||||
entry.__dict__[pro.name.replace('ga:','')] = pro
|
||||
for td in entry.tableId:
|
||||
td.__dict__['value'] = td.text
|
||||
return feed
|
||||
|
||||
class Dimension(gdata.GDataEntry):
|
||||
_tag = 'dimension'
|
||||
_namespace = GAN_NAMESPACE
|
||||
_children = gdata.GDataEntry._children.copy()
|
||||
_attributes = gdata.GDataEntry._attributes.copy()
|
||||
|
||||
_attributes['name'] = 'name'
|
||||
_attributes['value'] = 'value'
|
||||
_attributes['type'] = 'type'
|
||||
_attributes['confidenceInterval'] = 'confidence_interval'
|
||||
|
||||
def __init__(self, name=None, value=None, type=None,
|
||||
confidence_interval = None, *args, **kwargs):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.type = type
|
||||
self.confidence_interval = confidence_interval
|
||||
super(Dimension, self).__init__(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
def __repr__(self):
|
||||
return self.value
|
||||
|
||||
class Metric(gdata.GDataEntry):
|
||||
_tag = 'metric'
|
||||
_namespace = GAN_NAMESPACE
|
||||
_children = gdata.GDataEntry._children.copy()
|
||||
_attributes = gdata.GDataEntry._attributes.copy()
|
||||
|
||||
_attributes['name'] = 'name'
|
||||
_attributes['value'] = 'value'
|
||||
_attributes['type'] = 'type'
|
||||
_attributes['confidenceInterval'] = 'confidence_interval'
|
||||
|
||||
def __init__(self, name=None, value=None, type=None,
|
||||
confidence_interval = None, *args, **kwargs):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.type = type
|
||||
self.confidence_interval = confidence_interval
|
||||
super(Metric, self).__init__(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
def __repr__(self):
|
||||
return self.value
|
||||
|
||||
class AnalyticsDataEntry(gdata.GDataEntry):
|
||||
"""The Google Analytics version of an Atom Entry"""
|
||||
|
||||
_tag = 'entry'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.GDataEntry._children.copy()
|
||||
_attributes = gdata.GDataEntry._attributes.copy()
|
||||
|
||||
_children['{%s}dimension' % GAN_NAMESPACE] = ('dimension',
|
||||
[Dimension])
|
||||
|
||||
_children['{%s}metric' % GAN_NAMESPACE] = ('metric',
|
||||
[Metric])
|
||||
|
||||
def __init__(self, dimension=None, metric=None, *args, **kwargs):
|
||||
self.dimension = dimension
|
||||
self.metric = metric
|
||||
|
||||
super(AnalyticsDataEntry, self).__init__(*args, **kwargs)
|
||||
|
||||
class AnalyticsDataFeed(gdata.GDataFeed):
|
||||
"""A feed containing a list of Google Analytics Data Feed"""
|
||||
|
||||
_tag = 'feed'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.GDataFeed._children.copy()
|
||||
_attributes = gdata.GDataFeed._attributes.copy()
|
||||
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry',
|
||||
[AnalyticsDataEntry])
|
||||
|
||||
|
||||
"""
|
||||
Data Feed
|
||||
"""
|
||||
|
||||
def AnalyticsDataFeedFromString(xml_string):
|
||||
"""Converts an XML string into an AccountListFeed object.
|
||||
|
||||
Args:
|
||||
xml_string: string The XML describing an AccountList feed.
|
||||
|
||||
Returns:
|
||||
An AccountListFeed object corresponding to the given XML.
|
||||
Each metric and dimension is also referenced directly from
|
||||
the entry for easier access. (e.g. entry.keyword.value)
|
||||
"""
|
||||
feed = atom.CreateClassFromXMLString(AnalyticsDataFeed, xml_string)
|
||||
if feed.entry:
|
||||
for entry in feed.entry:
|
||||
for met in entry.metric:
|
||||
entry.__dict__[met.name.replace('ga:','')] = met
|
||||
if entry.dimension is not None:
|
||||
for dim in entry.dimension:
|
||||
entry.__dict__[dim.name.replace('ga:','')] = dim
|
||||
|
||||
return feed
|
313
python/gdata/analytics/client.py
Normal file
313
python/gdata/analytics/client.py
Normal file
@@ -0,0 +1,313 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2010 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Streamlines requests to the Google Analytics APIs."""
|
||||
|
||||
__author__ = 'api.nickm@google.com (Nick Mihailovski)'
|
||||
|
||||
|
||||
import atom.data
|
||||
import gdata.client
|
||||
import gdata.analytics.data
|
||||
import gdata.gauth
|
||||
|
||||
|
||||
class AnalyticsClient(gdata.client.GDClient):
|
||||
"""Client extension for the Google Analytics API service."""
|
||||
|
||||
api_version = '2'
|
||||
auth_service = 'analytics'
|
||||
auth_scopes = gdata.gauth.AUTH_SCOPES['analytics']
|
||||
account_type = 'GOOGLE'
|
||||
|
||||
def __init__(self, auth_token=None, **kwargs):
|
||||
"""Initializes a new client for the Google Analytics Data Export API.
|
||||
|
||||
Args:
|
||||
auth_token: gdata.gauth.ClientLoginToken, AuthSubToken, or
|
||||
OAuthToken (optional) Authorizes this client to edit the user's data.
|
||||
kwargs: The other parameters to pass to gdata.client.GDClient
|
||||
constructor.
|
||||
"""
|
||||
|
||||
gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs)
|
||||
|
||||
def get_account_feed(self, feed_uri, auth_token=None, **kwargs):
|
||||
"""Makes a request to the Analytics API Account Feed.
|
||||
|
||||
Args:
|
||||
feed_uri: str or gdata.analytics.AccountFeedQuery The Analytics Account
|
||||
Feed uri to define what data to retrieve from the API. Can also be
|
||||
used with a gdata.analytics.AccountFeedQuery object.
|
||||
"""
|
||||
|
||||
return self.get_feed(feed_uri,
|
||||
desired_class=gdata.analytics.data.AccountFeed,
|
||||
auth_token=auth_token,
|
||||
**kwargs)
|
||||
|
||||
GetAccountFeed = get_account_feed
|
||||
|
||||
def get_data_feed(self, feed_uri, auth_token=None, **kwargs):
|
||||
"""Makes a request to the Analytics API Data Feed.
|
||||
|
||||
Args:
|
||||
feed_uri: str or gdata.analytics.AccountFeedQuery The Analytics Data
|
||||
Feed uri to define what data to retrieve from the API. Can also be
|
||||
used with a gdata.analytics.AccountFeedQuery object.
|
||||
"""
|
||||
|
||||
return self.get_feed(feed_uri,
|
||||
desired_class=gdata.analytics.data.DataFeed,
|
||||
auth_token=auth_token,
|
||||
**kwargs)
|
||||
|
||||
GetDataFeed = get_data_feed
|
||||
|
||||
def get_management_feed(self, feed_uri, auth_token=None, **kwargs):
|
||||
"""Makes a request to the Google Analytics Management API.
|
||||
|
||||
The Management API provides read-only access to configuration data for
|
||||
Google Analytics and supercedes the Data Export API Account Feed.
|
||||
The Management API supports 5 feeds: account, web property, profile,
|
||||
goal, advanced segment.
|
||||
|
||||
You can access each feed through the respective management query class
|
||||
below. All requests return the same data object.
|
||||
|
||||
Args:
|
||||
feed_uri: str or AccountQuery, WebPropertyQuery,
|
||||
ProfileQuery, GoalQuery, MgmtAdvSegFeedQuery
|
||||
The Management API Feed uri to define which feed to retrieve.
|
||||
Either use a string or one of the wrapper classes.
|
||||
"""
|
||||
|
||||
return self.get_feed(feed_uri,
|
||||
desired_class=gdata.analytics.data.ManagementFeed,
|
||||
auth_token=auth_token,
|
||||
**kwargs)
|
||||
|
||||
GetMgmtFeed = GetManagementFeed = get_management_feed
|
||||
|
||||
|
||||
class AnalyticsBaseQuery(gdata.client.GDQuery):
|
||||
"""Abstracts common configuration across all query objects.
|
||||
|
||||
Attributes:
|
||||
scheme: string The default scheme. Should always be https.
|
||||
host: string The default host.
|
||||
"""
|
||||
|
||||
scheme = 'https'
|
||||
host = 'www.google.com'
|
||||
|
||||
|
||||
class AccountFeedQuery(AnalyticsBaseQuery):
|
||||
"""Account Feed query class to simplify constructing Account Feed Urls.
|
||||
|
||||
To use this class, you can either pass a dict in the constructor that has
|
||||
all the data feed query parameters as keys:
|
||||
queryUrl = AccountFeedQuery({'max-results': '10000'})
|
||||
|
||||
Alternatively you can add new parameters directly to the query object:
|
||||
queryUrl = AccountFeedQuery()
|
||||
queryUrl.query['max-results'] = '10000'
|
||||
|
||||
Args:
|
||||
query: dict (optional) Contains all the GA Data Feed query parameters
|
||||
as keys.
|
||||
"""
|
||||
|
||||
path = '/analytics/feeds/accounts/default'
|
||||
|
||||
def __init__(self, query={}, **kwargs):
|
||||
self.query = query
|
||||
gdata.client.GDQuery(self, **kwargs)
|
||||
|
||||
|
||||
class DataFeedQuery(AnalyticsBaseQuery):
|
||||
"""Data Feed query class to simplify constructing Data Feed Urls.
|
||||
|
||||
To use this class, you can either pass a dict in the constructor that has
|
||||
all the data feed query parameters as keys:
|
||||
queryUrl = DataFeedQuery({'start-date': '2008-10-01'})
|
||||
|
||||
Alternatively you can add new parameters directly to the query object:
|
||||
queryUrl = DataFeedQuery()
|
||||
queryUrl.query['start-date'] = '2008-10-01'
|
||||
|
||||
Args:
|
||||
query: dict (optional) Contains all the GA Data Feed query parameters
|
||||
as keys.
|
||||
"""
|
||||
|
||||
path = '/analytics/feeds/data'
|
||||
|
||||
def __init__(self, query={}, **kwargs):
|
||||
self.query = query
|
||||
gdata.client.GDQuery(self, **kwargs)
|
||||
|
||||
|
||||
class AccountQuery(AnalyticsBaseQuery):
|
||||
"""Management API Account Feed query class.
|
||||
|
||||
Example Usage:
|
||||
queryUrl = AccountQuery()
|
||||
queryUrl = AccountQuery({'max-results': 100})
|
||||
|
||||
queryUrl2 = AccountQuery()
|
||||
queryUrl2.query['max-results'] = 100
|
||||
|
||||
Args:
|
||||
query: dict (optional) A dictionary of query parameters.
|
||||
"""
|
||||
|
||||
path = '/analytics/feeds/datasources/ga/accounts'
|
||||
|
||||
def __init__(self, query={}, **kwargs):
|
||||
self.query = query
|
||||
gdata.client.GDQuery(self, **kwargs)
|
||||
|
||||
class WebPropertyQuery(AnalyticsBaseQuery):
|
||||
"""Management API Web Property Feed query class.
|
||||
|
||||
Example Usage:
|
||||
queryUrl = WebPropertyQuery()
|
||||
queryUrl = WebPropertyQuery('123', {'max-results': 100})
|
||||
queryUrl = WebPropertyQuery(acct_id='123',
|
||||
query={'max-results': 100})
|
||||
|
||||
queryUrl2 = WebPropertyQuery()
|
||||
queryUrl2.acct_id = '1234'
|
||||
queryUrl2.query['max-results'] = 100
|
||||
|
||||
Args:
|
||||
acct_id: string (optional) The account ID to filter results.
|
||||
Default is ~all.
|
||||
query: dict (optional) A dictionary of query parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, acct_id='~all', query={}, **kwargs):
|
||||
self.acct_id = acct_id
|
||||
self.query = query
|
||||
gdata.client.GDQuery(self, **kwargs)
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""Wrapper for path attribute."""
|
||||
return ('/analytics/feeds/datasources/ga/accounts/%s/webproperties' %
|
||||
self.acct_id)
|
||||
|
||||
|
||||
class ProfileQuery(AnalyticsBaseQuery):
|
||||
"""Management API Profile Feed query class.
|
||||
|
||||
Example Usage:
|
||||
queryUrl = ProfileQuery()
|
||||
queryUrl = ProfileQuery('123', 'UA-123-1', {'max-results': 100})
|
||||
queryUrl = ProfileQuery(acct_id='123',
|
||||
web_prop_id='UA-123-1',
|
||||
query={'max-results': 100})
|
||||
|
||||
queryUrl2 = ProfileQuery()
|
||||
queryUrl2.acct_id = '123'
|
||||
queryUrl2.web_prop_id = 'UA-123-1'
|
||||
queryUrl2.query['max-results'] = 100
|
||||
|
||||
Args:
|
||||
acct_id: string (optional) The account ID to filter results.
|
||||
Default is ~all.
|
||||
web_prop_id: string (optional) The web property ID to filter results.
|
||||
Default is ~all.
|
||||
query: dict (optional) A dictionary of query parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, acct_id='~all', web_prop_id='~all', query={}, **kwargs):
|
||||
self.acct_id = acct_id
|
||||
self.web_prop_id = web_prop_id
|
||||
self.query = query
|
||||
gdata.client.GDQuery(self, **kwargs)
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""Wrapper for path attribute."""
|
||||
return ('/analytics/feeds/datasources/ga/accounts/%s/webproperties'
|
||||
'/%s/profiles' % (self.acct_id, self.web_prop_id))
|
||||
|
||||
|
||||
class GoalQuery(AnalyticsBaseQuery):
|
||||
"""Management API Goal Feed query class.
|
||||
|
||||
Example Usage:
|
||||
queryUrl = GoalQuery()
|
||||
queryUrl = GoalQuery('123', 'UA-123-1', '555',
|
||||
{'max-results': 100})
|
||||
queryUrl = GoalQuery(acct_id='123',
|
||||
web_prop_id='UA-123-1',
|
||||
profile_id='555',
|
||||
query={'max-results': 100})
|
||||
|
||||
queryUrl2 = GoalQuery()
|
||||
queryUrl2.acct_id = '123'
|
||||
queryUrl2.web_prop_id = 'UA-123-1'
|
||||
queryUrl2.query['max-results'] = 100
|
||||
|
||||
Args:
|
||||
acct_id: string (optional) The account ID to filter results.
|
||||
Default is ~all.
|
||||
web_prop_id: string (optional) The web property ID to filter results.
|
||||
Default is ~all.
|
||||
profile_id: string (optional) The profile ID to filter results.
|
||||
Default is ~all.
|
||||
query: dict (optional) A dictionary of query parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, acct_id='~all', web_prop_id='~all', profile_id='~all',
|
||||
query={}, **kwargs):
|
||||
self.acct_id = acct_id
|
||||
self.web_prop_id = web_prop_id
|
||||
self.profile_id = profile_id
|
||||
self.query = query or {}
|
||||
gdata.client.GDQuery(self, **kwargs)
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""Wrapper for path attribute."""
|
||||
return ('/analytics/feeds/datasources/ga/accounts/%s/webproperties'
|
||||
'/%s/profiles/%s/goals' % (self.acct_id, self.web_prop_id,
|
||||
self.profile_id))
|
||||
|
||||
|
||||
class AdvSegQuery(AnalyticsBaseQuery):
|
||||
"""Management API Goal Feed query class.
|
||||
|
||||
Example Usage:
|
||||
queryUrl = AdvSegQuery()
|
||||
queryUrl = AdvSegQuery({'max-results': 100})
|
||||
|
||||
queryUrl1 = AdvSegQuery()
|
||||
queryUrl1.query['max-results'] = 100
|
||||
|
||||
Args:
|
||||
query: dict (optional) A dictionary of query parameters.
|
||||
"""
|
||||
|
||||
path = '/analytics/feeds/datasources/ga/segments'
|
||||
|
||||
def __init__(self, query={}, **kwargs):
|
||||
self.query = query
|
||||
gdata.client.GDQuery(self, **kwargs)
|
||||
|
365
python/gdata/analytics/data.py
Normal file
365
python/gdata/analytics/data.py
Normal file
@@ -0,0 +1,365 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2010 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Data model classes for parsing and generating XML for both the
|
||||
Google Analytics Data Export and Management APIs. Although both APIs
|
||||
operate on different parts of Google Analytics, they share common XML
|
||||
elements and are released in the same module.
|
||||
|
||||
The Management API supports 5 feeds all using the same ManagementFeed
|
||||
data class.
|
||||
"""
|
||||
|
||||
__author__ = 'api.nickm@google.com (Nick Mihailovski)'
|
||||
|
||||
|
||||
import gdata.data
|
||||
import atom.core
|
||||
import atom.data
|
||||
|
||||
|
||||
# XML Namespace used in Google Analytics API entities.
|
||||
DXP_NS = '{http://schemas.google.com/analytics/2009}%s'
|
||||
GA_NS = '{http://schemas.google.com/ga/2009}%s'
|
||||
GD_NS = '{http://schemas.google.com/g/2005}%s'
|
||||
|
||||
|
||||
class GetProperty(object):
|
||||
"""Utility class to simplify retrieving Property objects."""
|
||||
|
||||
def get_property(self, name):
|
||||
"""Helper method to return a propery object by its name attribute.
|
||||
|
||||
Args:
|
||||
name: string The name of the <dxp:property> element to retrieve.
|
||||
|
||||
Returns:
|
||||
A property object corresponding to the matching <dxp:property> element.
|
||||
if no property is found, None is returned.
|
||||
"""
|
||||
|
||||
for prop in self.property:
|
||||
if prop.name == name:
|
||||
return prop
|
||||
|
||||
return None
|
||||
|
||||
GetProperty = get_property
|
||||
|
||||
|
||||
class GetMetric(object):
|
||||
"""Utility class to simplify retrieving Metric objects."""
|
||||
|
||||
def get_metric(self, name):
|
||||
"""Helper method to return a propery value by its name attribute
|
||||
|
||||
Args:
|
||||
name: string The name of the <dxp:metric> element to retrieve.
|
||||
|
||||
Returns:
|
||||
A property object corresponding to the matching <dxp:metric> element.
|
||||
if no property is found, None is returned.
|
||||
"""
|
||||
|
||||
for met in self.metric:
|
||||
if met.name == name:
|
||||
return met
|
||||
|
||||
return None
|
||||
|
||||
GetMetric = get_metric
|
||||
|
||||
|
||||
class GetDimension(object):
|
||||
"""Utility class to simplify retrieving Dimension objects."""
|
||||
|
||||
def get_dimension(self, name):
|
||||
"""Helper method to return a dimention object by its name attribute
|
||||
|
||||
Args:
|
||||
name: string The name of the <dxp:dimension> element to retrieve.
|
||||
|
||||
Returns:
|
||||
A dimension object corresponding to the matching <dxp:dimension> element.
|
||||
if no dimension is found, None is returned.
|
||||
"""
|
||||
|
||||
for dim in self.dimension:
|
||||
if dim.name == name:
|
||||
return dim
|
||||
|
||||
return None
|
||||
|
||||
GetDimension = get_dimension
|
||||
|
||||
|
||||
class GaLinkFinder(object):
|
||||
"""Utility class to return specific links in Google Analytics feeds."""
|
||||
|
||||
def get_parent_links(self):
|
||||
"""Returns a list of all the parent links in an entry."""
|
||||
|
||||
links = []
|
||||
for link in self.link:
|
||||
if link.rel == link.parent():
|
||||
links.append(link)
|
||||
|
||||
return links
|
||||
|
||||
GetParentLinks = get_parent_links
|
||||
|
||||
def get_child_links(self):
|
||||
"""Returns a list of all the child links in an entry."""
|
||||
|
||||
links = []
|
||||
for link in self.link:
|
||||
if link.rel == link.child():
|
||||
links.append(link)
|
||||
|
||||
return links
|
||||
|
||||
GetChildLinks = get_child_links
|
||||
|
||||
def get_child_link(self, target_kind):
|
||||
"""Utility method to return one child link.
|
||||
|
||||
Returns:
|
||||
A child link with the given target_kind. None if the target_kind was
|
||||
not found.
|
||||
"""
|
||||
|
||||
for link in self.link:
|
||||
if link.rel == link.child() and link.target_kind == target_kind:
|
||||
return link
|
||||
|
||||
return None
|
||||
|
||||
GetChildLink = get_child_link
|
||||
|
||||
|
||||
class StartDate(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:startDate>"""
|
||||
_qname = DXP_NS % 'startDate'
|
||||
|
||||
|
||||
class EndDate(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:endDate>"""
|
||||
_qname = DXP_NS % 'endDate'
|
||||
|
||||
|
||||
class Metric(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:metric>"""
|
||||
_qname = DXP_NS % 'metric'
|
||||
name = 'name'
|
||||
type = 'type'
|
||||
value = 'value'
|
||||
confidence_interval = 'confidenceInterval'
|
||||
|
||||
|
||||
class Aggregates(atom.core.XmlElement, GetMetric):
|
||||
"""Analytics Data Feed <dxp:aggregates>"""
|
||||
_qname = DXP_NS % 'aggregates'
|
||||
metric = [Metric]
|
||||
|
||||
|
||||
class TableId(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:tableId>"""
|
||||
_qname = DXP_NS % 'tableId'
|
||||
|
||||
|
||||
class TableName(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:tableName>"""
|
||||
_qname = DXP_NS % 'tableName'
|
||||
|
||||
|
||||
class Property(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:property>"""
|
||||
_qname = DXP_NS % 'property'
|
||||
name = 'name'
|
||||
value = 'value'
|
||||
|
||||
|
||||
class Definition(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:definition>"""
|
||||
_qname = DXP_NS % 'definition'
|
||||
|
||||
|
||||
class Segment(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:segment>"""
|
||||
_qname = DXP_NS % 'segment'
|
||||
id = 'id'
|
||||
name = 'name'
|
||||
definition = Definition
|
||||
|
||||
|
||||
class Engagement(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:engagement>"""
|
||||
_qname = GA_NS % 'engagement'
|
||||
type = 'type'
|
||||
comparison = 'comparison'
|
||||
threshold_value = 'thresholdValue'
|
||||
|
||||
|
||||
class Step(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:step>"""
|
||||
_qname = GA_NS % 'step'
|
||||
number = 'number'
|
||||
name = 'name'
|
||||
path = 'path'
|
||||
|
||||
|
||||
class Destination(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:destination>"""
|
||||
_qname = GA_NS % 'destination'
|
||||
step = [Step]
|
||||
expression = 'expression'
|
||||
case_sensitive = 'caseSensitive'
|
||||
match_type = 'matchType'
|
||||
step1_required = 'step1Required'
|
||||
|
||||
|
||||
class Goal(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:goal>"""
|
||||
_qname = GA_NS % 'goal'
|
||||
destination = Destination
|
||||
engagement = Engagement
|
||||
number = 'number'
|
||||
name = 'name'
|
||||
value = 'value'
|
||||
active = 'active'
|
||||
|
||||
|
||||
class CustomVariable(atom.core.XmlElement):
|
||||
"""Analytics Data Feed <dxp:customVariable>"""
|
||||
_qname = GA_NS % 'customVariable'
|
||||
index = 'index'
|
||||
name = 'name'
|
||||
scope = 'scope'
|
||||
|
||||
|
||||
class DataSource(atom.core.XmlElement, GetProperty):
|
||||
"""Analytics Data Feed <dxp:dataSource>"""
|
||||
_qname = DXP_NS % 'dataSource'
|
||||
table_id = TableId
|
||||
table_name = TableName
|
||||
property = [Property]
|
||||
|
||||
|
||||
class Dimension(atom.core.XmlElement):
|
||||
"""Analytics Feed <dxp:dimension>"""
|
||||
_qname = DXP_NS % 'dimension'
|
||||
name = 'name'
|
||||
value = 'value'
|
||||
|
||||
|
||||
class AnalyticsLink(atom.data.Link):
|
||||
"""Subclass of link <link>"""
|
||||
target_kind = GD_NS % 'targetKind'
|
||||
|
||||
@classmethod
|
||||
def parent(cls):
|
||||
"""Parent target_kind"""
|
||||
return '%s#parent' % GA_NS[1:-3]
|
||||
|
||||
@classmethod
|
||||
def child(cls):
|
||||
"""Child target_kind"""
|
||||
return '%s#child' % GA_NS[1:-3]
|
||||
|
||||
|
||||
# Account Feed.
|
||||
class AccountEntry(gdata.data.GDEntry, GetProperty):
|
||||
"""Analytics Account Feed <entry>"""
|
||||
_qname = atom.data.ATOM_TEMPLATE % 'entry'
|
||||
table_id = TableId
|
||||
property = [Property]
|
||||
goal = [Goal]
|
||||
custom_variable = [CustomVariable]
|
||||
|
||||
|
||||
class AccountFeed(gdata.data.GDFeed):
|
||||
"""Analytics Account Feed <feed>"""
|
||||
_qname = atom.data.ATOM_TEMPLATE % 'feed'
|
||||
segment = [Segment]
|
||||
entry = [AccountEntry]
|
||||
|
||||
|
||||
# Data Feed.
|
||||
class DataEntry(gdata.data.GDEntry, GetMetric, GetDimension):
|
||||
"""Analytics Data Feed <entry>"""
|
||||
_qname = atom.data.ATOM_TEMPLATE % 'entry'
|
||||
dimension = [Dimension]
|
||||
metric = [Metric]
|
||||
|
||||
def get_object(self, name):
|
||||
"""Returns either a Dimension or Metric object with the same name as the
|
||||
name parameter.
|
||||
|
||||
Args:
|
||||
name: string The name of the object to retrieve.
|
||||
|
||||
Returns:
|
||||
Either a Dimension or Object that has the same as the name parameter.
|
||||
"""
|
||||
|
||||
output = self.GetDimension(name)
|
||||
if not output:
|
||||
output = self.GetMetric(name)
|
||||
|
||||
return output
|
||||
|
||||
GetObject = get_object
|
||||
|
||||
|
||||
class DataFeed(gdata.data.GDFeed):
|
||||
"""Analytics Data Feed <feed>.
|
||||
|
||||
Although there is only one datasource, it is stored in an array to replicate
|
||||
the design of the Java client library and ensure backwards compatibility if
|
||||
new data sources are added in the future.
|
||||
"""
|
||||
|
||||
_qname = atom.data.ATOM_TEMPLATE % 'feed'
|
||||
start_date = StartDate
|
||||
end_date = EndDate
|
||||
aggregates = Aggregates
|
||||
data_source = [DataSource]
|
||||
entry = [DataEntry]
|
||||
segment = Segment
|
||||
|
||||
|
||||
# Management Feed.
|
||||
class ManagementEntry(gdata.data.GDEntry, GetProperty, GaLinkFinder):
|
||||
"""Analytics Managememt Entry <entry>."""
|
||||
|
||||
_qname = atom.data.ATOM_TEMPLATE % 'entry'
|
||||
kind = GD_NS % 'kind'
|
||||
property = [Property]
|
||||
goal = Goal
|
||||
segment = Segment
|
||||
link = [AnalyticsLink]
|
||||
|
||||
|
||||
class ManagementFeed(gdata.data.GDFeed):
|
||||
"""Analytics Management Feed <feed>.
|
||||
|
||||
This class holds the data for all 5 Management API feeds: Account,
|
||||
Web Property, Profile, Goal, and Advanced Segment Feeds.
|
||||
"""
|
||||
|
||||
_qname = atom.data.ATOM_TEMPLATE % 'feed'
|
||||
entry = [ManagementEntry]
|
||||
kind = GD_NS % 'kind'
|
331
python/gdata/analytics/service.py
Normal file
331
python/gdata/analytics/service.py
Normal file
@@ -0,0 +1,331 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 2006 Google Inc.
|
||||
# Refactored in 2009 to work for Google Analytics by Sal Uryasev at Juice Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
AccountsService extends the GDataService to streamline Google Analytics
|
||||
account information operations.
|
||||
|
||||
AnalyticsDataService: Provides methods to query google analytics data feeds.
|
||||
Extends GDataService.
|
||||
|
||||
DataQuery: Queries a Google Analytics Data list feed.
|
||||
|
||||
AccountQuery: Queries a Google Analytics Account list feed.
|
||||
"""
|
||||
|
||||
|
||||
__author__ = 'api.suryasev (Sal Uryasev)'
|
||||
|
||||
|
||||
import urllib
|
||||
import atom
|
||||
import gdata.service
|
||||
import gdata.analytics
|
||||
|
||||
|
||||
class AccountsService(gdata.service.GDataService):
|
||||
|
||||
"""Client extension for the Google Analytics Account List feed."""
|
||||
|
||||
def __init__(self, email="", password=None, source=None,
|
||||
server='www.google.com/analytics', additional_headers=None,
|
||||
**kwargs):
|
||||
"""Creates a client for the Google Analytics service.
|
||||
|
||||
Args:
|
||||
email: string (optional) The user's email address, used for
|
||||
authentication.
|
||||
password: string (optional) The user's password.
|
||||
source: string (optional) The name of the user's application.
|
||||
server: string (optional) The name of the server to which a connection
|
||||
will be opened.
|
||||
**kwargs: The other parameters to pass to gdata.service.GDataService
|
||||
constructor.
|
||||
"""
|
||||
|
||||
gdata.service.GDataService.__init__(
|
||||
self, email=email, password=password, service='analytics',
|
||||
source=source, server=server, additional_headers=additional_headers,
|
||||
**kwargs)
|
||||
|
||||
def QueryAccountListFeed(self, uri):
|
||||
"""Retrieves an AccountListFeed by retrieving a URI based off the Document
|
||||
List feed, including any query parameters. An AccountListFeed object
|
||||
can be used to construct these parameters.
|
||||
|
||||
Args:
|
||||
uri: string The URI of the feed being retrieved possibly with query
|
||||
parameters.
|
||||
|
||||
Returns:
|
||||
An AccountListFeed object representing the feed returned by the server.
|
||||
"""
|
||||
return self.Get(uri, converter=gdata.analytics.AccountListFeedFromString)
|
||||
|
||||
def GetAccountListEntry(self, uri):
|
||||
"""Retrieves a particular AccountListEntry by its unique URI.
|
||||
|
||||
Args:
|
||||
uri: string The unique URI of an entry in an Account List feed.
|
||||
|
||||
Returns:
|
||||
An AccountLisFeed object representing the retrieved entry.
|
||||
"""
|
||||
return self.Get(uri, converter=gdata.analytics.AccountListEntryFromString)
|
||||
|
||||
def GetAccountList(self, max_results=1000, text_query=None,
|
||||
params=None, categories=None):
|
||||
"""Retrieves a feed containing all of a user's accounts and profiles."""
|
||||
q = gdata.analytics.service.AccountQuery(max_results=max_results,
|
||||
text_query=text_query,
|
||||
params=params,
|
||||
categories=categories);
|
||||
return self.QueryAccountListFeed(q.ToUri())
|
||||
|
||||
|
||||
|
||||
|
||||
class AnalyticsDataService(gdata.service.GDataService):
|
||||
|
||||
"""Client extension for the Google Analytics service Data List feed."""
|
||||
|
||||
def __init__(self, email=None, password=None, source=None,
|
||||
server='www.google.com/analytics', additional_headers=None,
|
||||
**kwargs):
|
||||
"""Creates a client for the Google Analytics service.
|
||||
|
||||
Args:
|
||||
email: string (optional) The user's email address, used for
|
||||
authentication.
|
||||
password: string (optional) The user's password.
|
||||
source: string (optional) The name of the user's application.
|
||||
server: string (optional) The name of the server to which a connection
|
||||
will be opened. Default value: 'docs.google.com'.
|
||||
**kwargs: The other parameters to pass to gdata.service.GDataService
|
||||
constructor.
|
||||
"""
|
||||
|
||||
gdata.service.GDataService.__init__(self,
|
||||
email=email, password=password, service='analytics', source=source,
|
||||
server=server, additional_headers=additional_headers, **kwargs)
|
||||
|
||||
def GetData(self, ids='', dimensions='', metrics='',
|
||||
sort='', filters='', start_date='',
|
||||
end_date='', start_index='',
|
||||
max_results=''):
|
||||
"""Retrieves a feed containing a user's data
|
||||
|
||||
ids: comma-separated string of analytics accounts.
|
||||
dimensions: comma-separated string of dimensions.
|
||||
metrics: comma-separated string of metrics.
|
||||
sort: comma-separated string of dimensions and metrics for sorting.
|
||||
This may be previxed with a minus to sort in reverse order.
|
||||
(e.g. '-ga:keyword')
|
||||
If ommited, the first dimension passed in will be used.
|
||||
filters: comma-separated string of filter parameters.
|
||||
(e.g. 'ga:keyword==google')
|
||||
start_date: start date for data pull.
|
||||
end_date: end date for data pull.
|
||||
start_index: used in combination with max_results to pull more than 1000
|
||||
entries. This defaults to 1.
|
||||
max_results: maximum results that the pull will return. This defaults
|
||||
to, and maxes out at 1000.
|
||||
"""
|
||||
q = gdata.analytics.service.DataQuery(ids=ids,
|
||||
dimensions=dimensions,
|
||||
metrics=metrics,
|
||||
filters=filters,
|
||||
sort=sort,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
start_index=start_index,
|
||||
max_results=max_results);
|
||||
return self.AnalyticsDataFeed(q.ToUri())
|
||||
|
||||
def AnalyticsDataFeed(self, uri):
|
||||
"""Retrieves an AnalyticsListFeed by retrieving a URI based off the
|
||||
Document List feed, including any query parameters. An
|
||||
AnalyticsListFeed object can be used to construct these parameters.
|
||||
|
||||
Args:
|
||||
uri: string The URI of the feed being retrieved possibly with query
|
||||
parameters.
|
||||
|
||||
Returns:
|
||||
An AnalyticsListFeed object representing the feed returned by the
|
||||
server.
|
||||
"""
|
||||
return self.Get(uri,
|
||||
converter=gdata.analytics.AnalyticsDataFeedFromString)
|
||||
|
||||
"""
|
||||
Account Fetching
|
||||
"""
|
||||
|
||||
def QueryAccountListFeed(self, uri):
|
||||
"""Retrieves an Account ListFeed by retrieving a URI based off the Account
|
||||
List feed, including any query parameters. A AccountQuery object can
|
||||
be used to construct these parameters.
|
||||
|
||||
Args:
|
||||
uri: string The URI of the feed being retrieved possibly with query
|
||||
parameters.
|
||||
|
||||
Returns:
|
||||
An AccountListFeed object representing the feed returned by the server.
|
||||
"""
|
||||
return self.Get(uri, converter=gdata.analytics.AccountListFeedFromString)
|
||||
|
||||
def GetAccountListEntry(self, uri):
|
||||
"""Retrieves a particular AccountListEntry by its unique URI.
|
||||
|
||||
Args:
|
||||
uri: string The unique URI of an entry in an Account List feed.
|
||||
|
||||
Returns:
|
||||
An AccountListEntry object representing the retrieved entry.
|
||||
"""
|
||||
return self.Get(uri, converter=gdata.analytics.AccountListEntryFromString)
|
||||
|
||||
def GetAccountList(self, username="default", max_results=1000,
|
||||
start_index=1):
|
||||
"""Retrieves a feed containing all of a user's accounts and profiles.
|
||||
The username parameter is soon to be deprecated, with 'default'
|
||||
becoming the only allowed parameter.
|
||||
"""
|
||||
if not username:
|
||||
raise Exception("username is a required parameter")
|
||||
q = gdata.analytics.service.AccountQuery(username=username,
|
||||
max_results=max_results,
|
||||
start_index=start_index);
|
||||
return self.QueryAccountListFeed(q.ToUri())
|
||||
|
||||
class DataQuery(gdata.service.Query):
|
||||
"""Object used to construct a URI to a data feed"""
|
||||
def __init__(self, feed='/feeds/data', text_query=None,
|
||||
params=None, categories=None, ids="",
|
||||
dimensions="", metrics="", sort="", filters="",
|
||||
start_date="", end_date="", start_index="",
|
||||
max_results=""):
|
||||
"""Constructor for Analytics List Query
|
||||
|
||||
Args:
|
||||
feed: string (optional) The path for the feed. (e.g. '/feeds/data')
|
||||
|
||||
text_query: string (optional) The contents of the q query parameter.
|
||||
This string is URL escaped upon conversion to a URI.
|
||||
params: dict (optional) Parameter value string pairs which become URL
|
||||
params when translated to a URI. These parameters are added to
|
||||
the query's items.
|
||||
categories: list (optional) List of category strings which should be
|
||||
included as query categories. See gdata.service.Query for
|
||||
additional documentation.
|
||||
ids: comma-separated string of analytics accounts.
|
||||
dimensions: comma-separated string of dimensions.
|
||||
metrics: comma-separated string of metrics.
|
||||
sort: comma-separated string of dimensions and metrics.
|
||||
This may be previxed with a minus to sort in reverse order
|
||||
(e.g. '-ga:keyword').
|
||||
If ommited, the first dimension passed in will be used.
|
||||
filters: comma-separated string of filter parameters
|
||||
(e.g. 'ga:keyword==google').
|
||||
start_date: start date for data pull.
|
||||
end_date: end date for data pull.
|
||||
start_index: used in combination with max_results to pull more than 1000
|
||||
entries. This defaults to 1.
|
||||
max_results: maximum results that the pull will return. This defaults
|
||||
to, and maxes out at 1000.
|
||||
|
||||
Yields:
|
||||
A DocumentQuery object used to construct a URI based on the Document
|
||||
List feed.
|
||||
"""
|
||||
self.elements = {'ids': ids,
|
||||
'dimensions': dimensions,
|
||||
'metrics': metrics,
|
||||
'sort': sort,
|
||||
'filters': filters,
|
||||
'start-date': start_date,
|
||||
'end-date': end_date,
|
||||
'start-index': start_index,
|
||||
'max-results': max_results}
|
||||
|
||||
gdata.service.Query.__init__(self, feed, text_query, params, categories)
|
||||
|
||||
def ToUri(self):
|
||||
"""Generates a URI from the query parameters set in the object.
|
||||
|
||||
Returns:
|
||||
A string containing the URI used to retrieve entries from the Analytics
|
||||
List feed.
|
||||
"""
|
||||
old_feed = self.feed
|
||||
self.feed = '/'.join([old_feed]) + '?' + \
|
||||
urllib.urlencode(dict([(key, value) for key, value in \
|
||||
self.elements.iteritems() if value]))
|
||||
new_feed = gdata.service.Query.ToUri(self)
|
||||
self.feed = old_feed
|
||||
return new_feed
|
||||
|
||||
|
||||
class AccountQuery(gdata.service.Query):
|
||||
"""Object used to construct a URI to query the Google Account List feed"""
|
||||
def __init__(self, feed='/feeds/accounts', start_index=1,
|
||||
max_results=1000, username='default', text_query=None,
|
||||
params=None, categories=None):
|
||||
"""Constructor for Account List Query
|
||||
|
||||
Args:
|
||||
feed: string (optional) The path for the feed. (e.g. '/feeds/documents')
|
||||
visibility: string (optional) The visibility chosen for the current
|
||||
feed.
|
||||
projection: string (optional) The projection chosen for the current
|
||||
feed.
|
||||
text_query: string (optional) The contents of the q query parameter.
|
||||
This string is URL escaped upon conversion to a URI.
|
||||
params: dict (optional) Parameter value string pairs which become URL
|
||||
params when translated to a URI. These parameters are added to
|
||||
the query's items.
|
||||
categories: list (optional) List of category strings which should be
|
||||
included as query categories. See gdata.service.Query for
|
||||
additional documentation.
|
||||
username: string (deprecated) This value should now always be passed as
|
||||
'default'.
|
||||
|
||||
Yields:
|
||||
A DocumentQuery object used to construct a URI based on the Document
|
||||
List feed.
|
||||
"""
|
||||
self.max_results = max_results
|
||||
self.start_index = start_index
|
||||
self.username = username
|
||||
gdata.service.Query.__init__(self, feed, text_query, params, categories)
|
||||
|
||||
def ToUri(self):
|
||||
"""Generates a URI from the query parameters set in the object.
|
||||
|
||||
Returns:
|
||||
A string containing the URI used to retrieve entries from the Account
|
||||
List feed.
|
||||
"""
|
||||
old_feed = self.feed
|
||||
self.feed = '/'.join([old_feed, self.username]) + '?' + \
|
||||
'&'.join(['max-results=' + str(self.max_results),
|
||||
'start-index=' + str(self.start_index)])
|
||||
new_feed = self.feed
|
||||
self.feed = old_feed
|
||||
return new_feed
|
Reference in New Issue
Block a user